综合资讯 技术文章 原文阅读 在线商城 下载专区 DATASHEET 技术论坛 商务频道

电子技术 | 技术资料 | 嵌入式系统 | 单片机专题 | DSP专题
EDA/PLD专题 | 电源技术专题 | 电子制作专题 | 其他综合 | 芯片选型

所在的位置:首页在线阅读嵌入式系统linux 笔记(一)正文
 
系统管理—安全—防火墙

  • netfilter
  • ip/tables
  • 防火墙技术分析讲义
  • 标题: A new place to LKM:netfilter
    作者: yawl <yawl@nsfocus.com>
    时间: 2000-10

    目录:
    -.前言
    二.分析
    三.例子代码
    四.附录:与2.2在应用方面的区别简介
    五.后记


    -.前言

      在linux2.2内核中的防火墙ipchains已经被用户广泛认可,它提供了完整的防火墙功能(包过滤,地址伪装,透明代理),又避免了商业防火墙那高的惊人的价格。如果你用的是某款国产防火墙,那么十有八九你实际在受到ipchains(有些甚至是2.0系列中ipfwadm)的保护:-).在未来的2.4内核中,被称为netfilter(http://netfilter.kernelnotes.org/)的防火墙以更好的结构重新构造,并实现了许多新功能,如完整的动态NAT(2.2内核实际是多对一的"地址伪装"),基于MAC及用户的过滤,真正的基于状态的过滤(不再是简单的查看tcp的标志位等),包速率限制等。
      在原有的网络部分的LKM中,如果对网络部分进行处理,一般是先生成struct packet_type结构,在用dev_add_pack将其插入网络层(注意此时的packet_type实际相当于一个的三层的协议,如ip_packet_type,ipx_8023_packet_type等),具体的例子可参见phrack 55期<Building into the linux network layer>和本月小四写的月刊文章<利用LLKM处理网络通信----对抗IDS、Firewall>。
      而netfilter本身在IP层内提供了另外的5个插入点(其文档中称为HOOK):NF_IP_PRE_ROUTING,NF_IP_LOCAL_IN,NF_IP_FORWARD,NF_IP_LOCAL_OUT,NF_IP_POST_ROUTING,分别对应IP层的五个不同位置,这样理论上在写lkm时便可以选择更适合的切入点,再辅以netfilter内置的新功能(如connect tracking),应该会帮助写出功能更强的lkm。
      本来准备写出一个完整的例子(限制IP连接数),但计划总赶不上变化:-(,只好先贴出个简单的例子来,权且自我安慰成抛砖引玉了。
      本文的参考配置是linux2.4.0-test4和iptable-1.1.1,好,开始抛砖,闪人喽!


    二.分析
      通俗的说,netfilter的架构就是在整个网络流程的若干位置放置了一些检测点(HOOK),而在每个检测点上上登记了一些处理函数进行处理(如包过滤,NAT等,甚至可以是用户自定义的功能)。
      IP层的五个HOOK点的位置如下图所示(copy from <packet filter howto>) :

              --->[1]--->[ROUTE]--->[3]--->[5]--->
                            |            ^
                            |            |
                            |         [ROUTE]
                            v            |
                           [2]          [4]
                            |            ^
                            |            |
                            v            |
                           [local process]

    [1]:NF_IP_PRE_ROUTING:刚刚进入网络层的数据包通过此点(刚刚进行完版本号,校验和等检测),源地址转换在此点
    进行;
    [2]:NF_IP_LOCAL_IN:经路由查找后,送往本机的通过此检查点,INPUT包过滤在此点进行;
    [3]:NF_IP_FORWARD:要转发的包通过此检测点,FORWORD包过滤在此点进行;
    [4]:NF_IP_LOCAL_OUT:本机进程发出的包通过此检测点,OUTPUT包过滤在此点进行;
    [5]:NF_IP_POST_ROUTING:所有马上便要通过网络设备出去的包通过此检测点,内置的目的地址转换功能(包括地址伪
    装)在此点进行。

      在IP层代码中,有一些带有NF_HOOK宏的语句,如IP的转发函数中有:

    <-ipforward.c ip_forward()->
    NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev2,
                           ip_forward_finish);

      其中NF_HOOK宏的定义提炼如下:

    <-/include/linux/netfilter.h->
    #ifdef CONFIG_NETFILTER
    #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)                        \
    (list_empty(&nf_hooks[(pf)][(hook)])                                        \
    ? (okfn)(skb)                                                                \
    : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
    #else /* !CONFIG_NETFILTER */
    #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
    #endif /*CONFIG_NETFILTER*/

      如果在编译内核时没有配置netfilter时,就相当于调用最后一个参数,此例中即执行ip_forward_finish函数;否则进入HOOK点,执行通过nf_register_hook()登记的功能(这句话表达的可能比较含糊,实际是进入
    nf_hook_slow()函数,再由它执行登记的函数)。

      NF_HOOK宏的参数分别为:
    1.pf:协议族名,netfilter架构同样可以用于IP层之外,因此这个变量还可以有诸如PF_INET6,PF_DECnet等名字。
    2.hook:HOOK点的名字,对于IP层,就是取上面的五个值;
    3.skb:不用多解释了吧;
    4.indev:进来的设备,以struct net_device结构表示;
    5.outdev:出去的设备,以struct net_device结构表示;
    (后面可以看到,以上五个参数将传到用nf_register_hook登记的处理函数中。)
    6.okfn:是个函数指针,当所有的该HOOK点的所有登记函数调用完后,转而走此流程。

      这些点是已经在内核中定义好的,除非你是这部分内核代码的维护者,否则无权增加或修改,而在此检测点进行的处理,则可由用户指定。像packet filter,NAT,connection track这些功能,也是以这种方式提供的。正如netfilter的当初的设计目标--提供一个完善灵活的框架,为扩展功能提供方便。

      如果我们想加入自己的代码,便要用nf_register_hook函数,其函数原型为:
      int nf_register_hook(struct nf_hook_ops *reg)
      我们考察一下struct nf_hook_ops结构:

    struct nf_hook_ops
    {
            struct list_head list;

            /* User fills in from here down. */
            nf_hookfn *hook;
            int pf;
            int hooknum;
            /* Hooks are ordered in ascending priority. */
            int priority;
    };

      我们的工作便是生成一个struct nf_hook_ops结构的实例,并用nf_register_hook将其HOOK上。其中list项我们总要初始化为{NULL,NULL};由于一般在IP层工作,pf总是PF_INET;hooknum就是我们选择的HOOK点;一个HOOK点可能挂多个处理函数,谁先谁后,便要看优先级,即priority的指定了。netfilter_ipv4.h中用一个枚举类型指定了内置的处理函数的优先级:

    enum nf_ip_hook_priorities {
            NF_IP_PRI_FIRST = INT_MIN,
            NF_IP_PRI_CONNTRACK = -200,
            NF_IP_PRI_MANGLE = -150,
            NF_IP_PRI_NAT_DST = -100,
            NF_IP_PRI_FILTER = 0,
            NF_IP_PRI_NAT_SRC = 100,
            NF_IP_PRI_LAST = INT_MAX,
    };

      hook是提供的处理函数,也就是我们的主要工作,其原型为:

    unsigned int nf_hookfn(unsigned int hooknum,
                                   struct sk_buff **skb,
                                   const struct net_device *in,
                                   const struct net_device *out,
                                   int (*okfn)(struct sk_buff *));

      它的五个参数将由NFHOOK宏传进去。
      了解了这些,基本上便可以可以写一个lkm出来了。

    三.例子代码

    这段代码是一个例子,其功能实现了一个IDS,检测几个简单攻击(land,winnuke)和特殊扫描(nmap),当然,不会有人真把
    它当严肃的IDS使用吧:-)。可以利用类似结构干点别的。。。

    <-example.c begin->

    /*
    *  netfilter module example: it`s a kernel IDS(be quie,donot laugh, my friend)
    *  yawl@nsfocus.com
    *  Compile:gcc -O -c -Wall sample.c ,under linux2.4 kernel,netfilter is needed.
    */

    #define __KERNEL__
    #define MODULE

    #include <linux/module.h>
    #include <linux/skbuff.h>
    #include <linux/netdevice.h>
    #include <linux/config.h>
    #include <linux/ip.h>
    #include <linux/tcp.h>
    #include <linux/udp.h>
    #include <linux/netfilter_ipv4.h>

    #define ALERT(fmt,args...) printk("nsfocus: " fmt, ##args)
    /*message will be print to screen(too many~),and logged to /var/log/message*/

    static unsigned int sample(unsigned int hooknum,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;
            struct udphdr *udph;

            __u32        sip;
            __u32        dip;
            __u16        sport;
            __u16        dport;

            iph=(*skb)->nh.iph;
            sip=iph->saddr;
            dip=iph->daddr;

            /*play ip packet here
            (note:checksum has been checked,if connection track is enabled,defrag have been done )*/
            if(iph->ihl!=5){
                    ALERT("IP packet with packet from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
            }

            if(iph->protocol==6){
                    tcph=(struct tcphdr*)((__u32 *)iph+iph->ihl);
                    sport=tcph->source;
                    dport=tcph->dest;
                    /*play tcp packet here*/
                    if((tcph->syn)&&(sport==dport)&&(sip==dip)){
                            ALERT("maybe land attack\n");
                    }
                    if(ntohs(tcph->dest)==139&&tcph->urg){
                            ALERT("maybe winnuke a from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    }
                    if(tcph->ece&&tcph->cwr){
                            ALERT("queso from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    }
                       if((tcph->fin)&&(tcph->syn)&&(!tcph->rst)&&(!tcph->psh)&&(!tcph->ack)&&(!tcph->urg)){
                            ALERT("SF_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    }
                       if((!tcph->fin)&&(!tcph->syn)&&(!tcph->rst)&&(!tcph->psh)&&(!tcph->ack)&&(!tcph->urg)){
                            ALERT("NULL_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    }
                       if(tcph->fin&&tcph->syn&&tcph->rst&&tcph->psh&&tcph->ack&&tcph->urg){
                            ALERT("FULL_Xmas_scan from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    }
                       if((tcph->fin)&&(!tcph->syn)&&(!tcph->rst)&&(tcph->psh)&&(!tcph->ack)&&(tcph->urg)){
                            ALERT("XMAS_Scan(FPU)from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    }
            }

            else if(iph->protocol==17){
                    udph=(struct udphdr *)((__u32 *)iph+iph->ihl);
                    sport=udph->source;
                    dport=udph->dest;
                    /*play udp packet here*/
            }

            else if(iph->protocol==1){
                    /*play icmp packet here*/
            }

            else if(iph->protocol==2){
                    ALERT("igmp packet from %d.%d.%d.%d to %d.%d.%d.%d\n",NIPQUAD(sip),NIPQUAD(dip));
                    /*play igmp packet here*/
            }

            else{
                    ALERT("unknown protocol%d packet from %d.%d.%d.%d to %d.%d.%d.%d\n",iph->protocol,NIPQUAD(sip),NIPQUAD(dip));
            }
            return NF_ACCEPT;
            /*for it is IDS,we just accept all packet,
            if you really want to drop this skb,just return NF_DROP*/

    }

    static struct nf_hook_ops iplimitfilter
    ={ {NULL,NULL} ,sample,PF_INET,NF_IP_PRE_ROUTING,NF_IP_PRI_FILTER-1};

    int init_module(void)
    {
            return nf_register_hook(&iplimitfilter);
    }

    void cleanup_module(void)
    {
            nf_unregister_hook(&iplimitfilter);
    }

    <-example.c end->

    四.附录:与2.2在应用方面的区别简介

      本来还想详细介绍一下iptables的用法,但如果说的太详细的话,还不如索性将HOWTO翻译一下,于是干脆了却了这个念头,只顺便简介一下与以前版本的变化(而且是我认为最重要的)。如果ipchains本来便没有在你的脑子中扎根,其实便没有必要看这部分。
      netfilter,又可称为iptables.开发初期准备将packet filter和NAT的配置工具完全分开,一个称为iptables,另一个称为ipnatctl,而将整个项目成为netfilter.但后来可能是还是习惯2.2内核中用ipchians一个工具干两件事的用法,又改为全部用iptables配置了。
      理论上还可以用2.2系列的ipchains和2.0系列的ipfwadm作配置工具,但只是做兼容或过渡的考虑了。通过源码看到他们也是通过现有的结构HOOK上去的(主要是net/ipv4/netfilter目录下的ip_fw_compat.c,ip_fw_compat_masq.c,
    ip_fw_compat_redir.c,ipchains_core.c,ipfwadm_core.c这几个文件)。
      一个重要的变化是原有的INPUT,OUTPUT(原来是小写的input,ouput)链(现在应称为表?)的位置变了,原来的input,output的位置相当于现在的NF_IP_PRE_ROUTING,NF_IP_POST_ROUTING 。原有的结构确实不太合理,转发的包要经过三条链,现在INPUT专指去往本机的,OUPUT专指从本机发出的,而FOWARD仍旧是转发的包。
      举两个简单的例子:
      1.作地址伪装(场景:对外通过拨号连接internet)注意原来的MASQ变成好长的MASQUERATE,而伪装相当于SNAT,因此位置是在POSTROUTING:

      iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERATE

      2.还有一个限制包速率的功能比较好玩,例如如下规则:

      iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT

      简单的说就是在转发时(-A FORWARD:因为是防火墙嘛),如果是tcp协议,且有syn标记(-p tcp --syn),可以限制为每秒一个(-m limit --limit 1/s ),行动项是ACCEPT。最后连起来意义就是每秒只允许转发一个tcp连接请求。


    五.后记
      netfilter还提供了许多新功能,如可以将包转发到应用层,由应用程序进行处理等,可目前我还没有分析多少,慢慢抽出点时间看吧。唉,尽管以前看过ipchains的代码,但netfilter实在变动太大了,一切都要从头看起:-(
      最后,当然要感谢Rusty Russell,netfilter项目的负责人,不仅为我们提供了这个强大好用的工具,还写了大量非常优秀的文档。


    参考文献:

    [1.] Linux 2.4 Packet Filtering HOWTO
            Rusty Russell, mailing list netfilter@lists.samba.org
              v1.0.1 Mon May  1 18:09:31 CST 2000
    [2.] Linux IPCHAINS-HOWTO
              Paul Russell, ipchains@rustcorp.com
              v1.0.7, Fri Mar 12 13:46:20 CST 1999
    [3.] Linux 2.4 NAT HOWTO
              Rusty Russell, mailing list netfilter@lists.samba.org
              v1.0.1 Mon May  1 18:38:22 CST 2000
    [4.] Linux netfilter Hacking HOWTO
              Rusty Russell, mailing list netfilter@lists.samba.org
              v1.0.1 Sat Jul  1 18:24:41 EST 2000
    [5.] Writing a Module for netfilter
            by Paul "Rusty" Russell
            Linux Magazine June 2000 http://www.linux-mag.com/2000-06/gear_01.html
    [6.] Salvatore Sanfilippo<antirez@invece.org>写的一份netfilter sample,但可惜我找不到出处了,只剩下手头一份打印稿,But anyway,thanks to Salvatore.

    ip/tables


      1 /*
      2  * 25-Jul-1998 Major changes to allow for ip chain table
      3  *
      4  * 3-Jan-2000 Named tables to allow packet selection for different uses.
      5  */
      6
      7 /*
      8  *      Format of an IP firewall descriptor
      9  *
    注意这里的说明:IP地址和掩码是按照网络字节存储(大端存储)标志字节和端口号是按照主机字节序存储(依主机硬件结构而定)
    10  *      src, dst, src_mask, dst_mask are always stored in network byte order.
    11  *      flags are stored in host byte order (of course).
    12  *      Port numbers are stored in HOST byte order.
    13  */
    14
    15 #ifndef _IPTABLES_H
    16 #define _IPTABLES_H
    17
    18 #ifdef __KERNEL__
    19 #include linux/if.h
    20 #include linux/types.h
    21 #include linux/in.h
    22 #include linux/ip.h
    23 #include linux/skbuff.h
    24 #endif
    25 #include linux/netfilter_ipv4.h
    26
    27 #define IPT_FUNCTION_MAXNAMELEN 30
    28 #define IPT_TABLE_MAXNAMELEN 32
    29
    这个结构存储与IP头部有关的防火墙规则信息。这里的注释说“这个结构无须填充零字节”,就是说这个结构的大小正好是4的倍数。这里由于IFNAMSIZ等于16,所以整个结构大小确实是4的倍数。
    30 /* Yes, Virginia, you have to zero the padding. */
    31 struct ipt_ip {
    32         /* Source and destination IP addr */
    33         struct in_addr src, dst;
    34         /* Mask for src and dest IP addr */
    35         struct in_addr smsk, dmsk;
    36         char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
    37         unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];
    38
    39         /* Protocol, 0 = ANY */
    40         u_int16_t proto;
    41
    42         /* Flags word */
    43         u_int8_t flags;
    44         /* Inverse flags */
    45         u_int8_t invflags;
    46 };
    47这个结构存储match的信息,这里的匹配主要是指与IP无关的防火墙规则信息。由系统缺省设置的匹配主要有三个“tcp”、“udp”,“icmp”,我在分析ip_tables.c时将详细描述。
    48 struct ipt_entry_match
    49 {
    50         union {
    51                 struct {
    52                         u_int16_t match_size;
    53
    54                         /* Used by userspace */
    55                         char name[IPT_FUNCTION_MAXNAMELEN];
    56                 } user;
    57                 struct {
    58                         u_int16_t match_size;
    59
    60                         /* Used inside the kernel */
    61                         struct ipt_match *match;
    62                 } kernel;
    63
    64                 /* Total length */
    65                 u_int16_t match_size;
    66         } u;
    67
    68         unsigned char data[0];
    69 };
    70 target结构信息,是决定一个分组命运的信息。也可以理解为action信息,其意义是指当一个分组与rule和match信息匹配后,如何处置该分组。处置方法一般有三种:一,命令常数,比如DROP ACCEPT等等;二 系统预定义的模块处理函数,比如”SNAT DNAT"等等;第三种是用户自己写模块函数。
    71 struct ipt_entry_target
    72 {
    73         union {
    74                 struct {
    75                         u_int16_t target_size;
    76
    77                         /* Used by userspace */
    78                         char name[IPT_FUNCTION_MAXNAMELEN];
    79                 } user;
    80                 struct {
    81                         u_int16_t target_size;
    82
    83                         /* Used inside the kernel */
    84                         struct ipt_target *target;
    85                 } kernel;
    86
    87                 /* Total length */
    88                 u_int16_t target_size;
    89         } u;
    90
    91         unsigned char data[0];
    92 };
    93这个结构已经很明显给出了target的形式:命令常数、或者模块函数。
    94 struct ipt_standard_target
    95 {
    96         struct ipt_entry_target target;
    97         int verdict;
    98 };
    99
    计数器结构,每一个rule都有一个计数器结构用来统计匹配该条规则的分组数目和字节数目。为基于统计的安全工具提供分析基础。
    100 struct ipt_counters
    101 {
    102         u_int64_t pcnt, bcnt;                   /* Packet and byte counters */
    103 };
    104
    标志字段,各个常数后面的注释已经给出了明确的解释,这里不再赘述。
    105 /* Values for "flag" field in struct ipt_ip (general ip structure). */
    106 #define IPT_F_FRAG              0x01    /* Set if rule is a fragment rule */
    107 #define IPT_F_MASK              0x01    /* All possible flag bits mask. */
    108
    109 /* Values for "inv" field in struct ipt_ip. */
    110 #define IPT_INV_VIA_IN          0x01    /* Invert the sense of IN IFACE. */
    111 #define IPT_INV_VIA_OUT         0x02    /* Invert the sense of OUT IFACE */
    112 #define IPT_INV_TOS             0x04    /* Invert the sense of TOS. */
    113 #define IPT_INV_SRCIP           0x08    /* Invert the sense of SRC IP. */
    114 #define IPT_INV_DSTIP           0x10    /* Invert the sense of DST OP. */
    115 #define IPT_INV_FRAG            0x20    /* Invert the sense of FRAG. */
    116 #define IPT_INV_PROTO           0x40    /* Invert the sense of PROTO. */
    掩码标志。用法是当出现超出掩码范围的标志时,确认是非法标志。
    117 #define IPT_INV_MASK            0x7F    /* All possible flag bits mask. */
    118
    其实这个结构的构成这里的注释已经说的很清楚,但是从论坛上有人问"关于netfilter的问题“时,可以看出很多人还是不理解。与前面ipchains版本防火墙不同的是iptables的防火墙规则构成发生了变化。ipchains的构成是rule+target,而iptables的构成是ip匹配信息+match+target。同时iptables构成的每一个部分都是可变大小的,由于经常出现”char XXX[0]“就可以看出。但是我个人认为规则的组织有点不好理解,它经常是先分配一段空间,然后将规则一条一条放入。如同文件系统存放变长记录的文件时,总要在记录中放入记录长度,以便以后取出记录,这里iptables正是使用这种方法,在每个规则中都放入长度字段,这样方便提取各个组成部分和计算下一条规则的位置。
    119 /* This structure defines each of the firewall rules.  Consists of 3
    120    parts which are 1) general IP header stuff 2) match specific
    121    stuff 3) the target to perform if the rule matches */
    122 struct ipt_entry
    123 {
    124         struct ipt_ip ip;
    125
    126         /* Mark with fields that we care about. */
    127         unsigned int nfcache;
    128下面两个字段用来计算target的位置和下一条规则的位置。
    129         /* Size of ipt_entry + matches */
    130         u_int16_t target_offset;
    131         /* Size of ipt_entry + matches + target */
    132         u_int16_t next_offset;
    133
    这个字段的存在,为发现规则中存在”环路“提供手段。
    134         /* Back pointer */
    135         unsigned int comefrom;
    136
    137         /* Packet and byte counters. */
    138         struct ipt_counters counters;
    139
    140         /* The matches (if any), then the target. */
    141         unsigned char elems[0];
    142 };
    143
    144 /*
    145  * New IP firewall options for [gs]etsockopt at the RAW IP level.
    146  * Unlike BSD Linux inherits IP options so you don't have to use a raw
    147  * socket for this. Instead we check rights in the calls. */
    定义提供给set/getsockopt系统调用的命令常数的基常数。
    148 #define IPT_BASE_CTL            64      /* base for firewall socket options */
    149
    150 #define IPT_SO_SET_REPLACE      (IPT_BASE_CTL)
    151 #define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1)
    152 #define IPT_SO_SET_MAX          IPT_SO_SET_ADD_COUNTERS
    153
    154 #define IPT_SO_GET_INFO         (IPT_BASE_CTL)
    155 #define IPT_SO_GET_ENTRIES      (IPT_BASE_CTL + 1)
    156 #define IPT_SO_GET_MAX          IPT_SO_GET_ENTRIES
    157
    158 /* CONTINUE verdict for targets */
    159 #define IPT_CONTINUE 0xFFFFFFFF
    160
    161 /* For standard target */
    162 #define IPT_RETURN (-NF_MAX_VERDICT - 1)
    163
    Tcp匹配规则信息。
    164 /* TCP matching stuff */
    165 struct ipt_tcp
    166 {
    167         u_int16_t spts[2];                      /* Source port range. */
    168         u_int16_t dpts[2];                      /* Destination port range. */
    169         u_int8_t option;                        /* TCP Option iff non-zero*/
    170         u_int8_t flg_mask;                      /* TCP flags mask byte */
    171         u_int8_t flg_cmp;                       /* TCP flags compare byte */
    172         u_int8_t invflags;                      /* Inverse flags */
    173 };
    174
    tcp的取反标志值。
    175 /* Values for "inv" field in struct ipt_tcp. */
    176 #define IPT_TCP_INV_SRCPT       0x01    /* Invert the sense of source ports. */
    177 #define IPT_TCP_INV_DSTPT       0x02    /* Invert the sense of dest ports. */
    178 #define IPT_TCP_INV_FLAGS       0x04    /* Invert the sense of TCP flags. */
    179 #define IPT_TCP_INV_OPTION      0x08    /* Invert the sense of option test. */
    180 #define IPT_TCP_INV_MASK        0x0F    /* All possible flags. */
    181
    udp匹配规则信息
    182 /* UDP matching stuff */
    183 struct ipt_udp
    184 {
    185         u_int16_t spts[2];                      /* Source port range. */
    186         u_int16_t dpts[2];                      /* Destination port range. */
    187         u_int8_t invflags;                      /* Inverse flags */
    188 };
    189
    190 /* Values for "invflags" field in struct ipt_udp. */
    191 #define IPT_UDP_INV_SRCPT       0x01    /* Invert the sense of source ports. */
    192 #define IPT_UDP_INV_DSTPT       0x02    /* Invert the sense of dest ports. */
    193 #define IPT_UDP_INV_MASK        0x03    /* All possible flags. */
    194
    195 /* ICMP matching stuff */
    ICMP匹配规则信息
    196 struct ipt_icmp
    197 {
    198         u_int8_t type;                          /* type to match */
    199         u_int8_t code[2];                       /* range of code */
    200         u_int8_t invflags;                      /* Inverse flags */
    201 };
    202
    203 /* Values for "inv" field for struct ipt_icmp. */
    204 #define IPT_ICMP_INV    0x01    /* Invert the sense of type/code test */
    205
    这个结构实质上用户通过getsockopt系统调用获取table信息时所传递参数的类型。
    206 /* The argument to IPT_SO_GET_INFO */
    207 struct ipt_getinfo
    208 {
    209         /* Which table: caller fills this in. */
    210         char name[IPT_TABLE_MAXNAMELEN];
    211
    212         /* Kernel fills these in. */
    213         /* Which hook entry points are valid: bitmask */
    214         unsigned int valid_hooks;
    215
    216         /* Hook entry points: one per netfilter hook. */
    217         unsigned int hook_entry[NF_IP_NUMHOOKS];
    218
    219         /* Underflow points. */
    220         unsigned int underflow[NF_IP_NUMHOOKS];
    221
    222         /* Number of entries */
    223         unsigned int num_entries;
    224
    225         /* Size of entries. */
    226         unsigned int size;
    227 };
    228
    这个结构是用户通过系统调用更换table是所传递的参数类型。
    229 /* The argument to IPT_SO_SET_REPLACE. */
    230 struct ipt_replace
    231 {
    232         /* Which table. */
    233         char name[IPT_TABLE_MAXNAMELEN];
    234
    235         /* Which hook entry points are valid: bitmask.  You can't
    236            change this. */
    237         unsigned int valid_hooks;
    238
    239         /* Number of entries */
    240         unsigned int num_entries;
    241
    242         /* Total size of new entries */
    243         unsigned int size;
    244
    245         /* Hook entry points. */
    246         unsigned int hook_entry[NF_IP_NUMHOOKS];
    247
    248         /* Underflow points. */
    249         unsigned int underflow[NF_IP_NUMHOOKS];
    250
    251         /* Information about old entries: */
    252         /* Number of counters (must be equal to current number of entries). */
    253         unsigned int num_counters;
    254         /* The old entries' counters. */
    255         struct ipt_counters *counters;
    256
    257         /* The entries (hang off end: not really an array). */
    258         struct ipt_entry entries[0];
    259 };
    260
    这个更改计数器时传递的参数类型。
    261 /* The argument to IPT_SO_ADD_COUNTERS. */
    262 struct ipt_counters_info
    263 {
    264         /* Which table. */
    265         char name[IPT_TABLE_MAXNAMELEN];
    266
    267         unsigned int num_counters;
    268
    269         /* The counters (actually `number' of these). */
    270         struct ipt_counters counters[0];
    271 };
    272
    这个是想获取防火墙规则时,传递给系统调用的参数类型。
    273 /* The argument to IPT_SO_GET_ENTRIES. */
    274 struct ipt_get_entries
    275 {
    276         /* Which table: user fills this in. */
    277         char name[IPT_TABLE_MAXNAMELEN];
    278
    279         /* User fills this in: total entry size. */
    280         unsigned int size;
    281
    282         /* The entries. */
    283         struct ipt_entry entrytable[0];
    284 };
    285
    286 /* Standard return verdict, or do jump. */
    287 #define IPT_STANDARD_TARGET ""
    288 /* Error verdict. */
    289 #define IPT_ERROR_TARGET "ERROR"
    290
    现面定义了一些使用例程
    291 /* Helper functions */
    获取一条防火墙规则的target位置
    292 extern __inline__ struct ipt_entry_target *
    293 ipt_get_target(struct ipt_entry *e)
    294 {
    295         return (void *)e + e->target_offset;
    296 }
    297
    下面的宏遍历处理一条防火墙规则的所有匹配。我已经说过每一条防火墙规则在iptables中分为三部分,而且每一部分的大小都是可变的。比如match部分,它本身可以有多个match项。
    298 /* fn returns 0 to continue iteration */
    299 #define IPT_MATCH_ITERATE(e, fn, args...)       \
    300 ({                                              \
    301         unsigned int __i;                       \
    302         int __ret = 0;                          \
    303         struct ipt_entry_match *__m;            \

    这个for语句我来解释一下:首先__i取值为ipt_entry结构的大小,实质上就是match匹配的开始处的偏移地址,将其与e相加就得到了match匹配的地址,然后调用fn处理这个匹配。如果函数返回值为零,当前匹配的偏移地址加上当前匹配的大小,如果不超过target的偏移地址,则继续处理下一条匹配。
    304                                                 \
    305         for (__i = sizeof(struct ipt_entry);    \
    306              __i target_offset;          \
    307              __i += __m->u.match_size) {        \
    308                 __m = (void *)(e) + __i;        \
    309                                                 \
    310                 __ret = fn(__m , ## args);      \
    311                 if (__ret != 0)                 \
    312                         break;                  \
    313         }                                       \
    314         __ret;                                  \
    315 })
    316
    这个宏处理一个table中的所有防火墙规则。对比对上一个宏的理解,这里我就不解释了。
    317 /* fn returns 0 to continue iteration */
    318 #define IPT_ENTRY_ITERATE(entries, size, fn, args...)           \
    319 ({                                                              \
    320         unsigned int __i;                                       \
    321         int __ret = 0;                                          \
    322         struct ipt_entry *__e;                                  \
    323                                                                 \
    324         for (__i = 0; __i next_offset) {  \
    325                 __e = (void *)(entries) + __i;                  \
    326                                                                 \
    327                 __ret = fn(__e , ## args);                      \
    328                 if (__ret != 0)                                 \
    329                         break;                                  \
    330         }                                                       \
    331         __ret;                                                  \
    332 })
    333
    334 /*
    335  *      Main firewall chains definitions and global var's definitions.
    336  */
    337 #ifdef __KERNEL__
    338
    339 #include ·
    340 extern void ipt_init(void) __init;
    341
    所有的匹配处理都注册到一个match处理链表中,链表结点的类型就是这里的结构类型。当处理匹配时都是调用这里注册的处理函数。每个结点实质上由三个函数构成,一个匹配处理函数,一个合法性检查函数,一个析构函数。最后一个是反身指针,指针的作用如注释所示。
    342 struct ipt_match
    343 {
    344         struct list_head list;
    345
    346         const char name[IPT_FUNCTION_MAXNAMELEN];
    347
    348         /* Return true or false: return FALSE and set *hotdrop = 1 to
    349            force immediate packet drop. */
    350         int (*match)(const struct sk_buff *skb,
    351                      const struct net_device *in,
    352                      const struct net_device *out,
    353                      const void *matchinfo,
    354                      int offset,
    355                      const void *hdr,
    356                      u_int16_t datalen,
    357                      int *hotdrop);
    358
    359         /* Called when user tries to insert an entry of this type. */
    360         /* Should return true or false. */
    361         int (*checkentry)(const char *tablename,
    362                           const struct ipt_ip *ip,
    363                           void *matchinfo,
    364                           unsigned int matchinfosize,
    365                           unsigned int hook_mask);
    366
    367         /* Called when entry of this type deleted. */
    368         void (*destroy)(void *matchinfo, unsigned int matchinfosize);
    369
    370         /* Set this to THIS_MODULE if you are a module, otherwise NULL */
    371         struct module *me;
    372 };
    373
    和match一样,所有的target都注册到这个结构类型的全局链表中,每个target的处理函数都是这里注册的函数。和上面的解释一样,这里也主要包含三个函数指针。
    374 /* Registration hooks for targets. */
    375 struct ipt_target
    376 {
    377         struct list_head list;
    378
    379         const char name[IPT_FUNCTION_MAXNAMELEN];
    380
    381         /* Returns verdict. */
    382         unsigned int (*target)(struct sk_buff **pskb,
    383                                unsigned int hooknum,
    384                                const struct net_device *in,
    385                                const struct net_device *out,
    386                                const void *targinfo,
    387                                void *userdata);
    388
    389         /* Called when user tries to insert an entry of this type:
    390            hook_mask is a bitmask of hooks from which it can be
    391            called. */
    392         /* Should return true or false. */
    393         int (*checkentry)(const char *tablename,
    394                           const struct ipt_entry *e,
    395                           void *targinfo,
    396                           unsigned int targinfosize,
    397                           unsigned int hook_mask);
    398
    399         /* Called when entry of this type deleted. */
    400         void (*destroy)(void *targinfo, unsigned int targinfosize);
    401
    402         /* Set this to THIS_MODULE if you are a module, otherwise NULL */
    403         struct module *me;
    404 };
    405
    注册函数
    406 extern int ipt_register_target(struct ipt_target *target);
    407 extern void ipt_unregister_target(struct ipt_target *target);
    408
    409 extern int ipt_register_match(struct ipt_match *match);
    410 extern void ipt_unregister_match(struct ipt_match *match);
    411
    table结构
    412 /* Furniture shopping... */
    413 struct ipt_table
    414 {
    415         struct list_head list;
    416
    417         /* A unique name... */
    418         char name[IPT_TABLE_MAXNAMELEN];
    419
    420         /* Seed table: copied in register_table */
    421         struct ipt_replace *table;
    422
    423         /* What hooks you will enter on */
    424         unsigned int valid_hooks;
    425
    426         /* Lock for the curtain */
    427         rwlock_t lock;
    428
    429         /* Man behind the curtain... */
    430         struct ipt_table_info *private;
    431 };
    432
    小结iptables的防火墙组织结构:

    现在我们可以给出iptables防火墙规则的组织结构了。第一级是table级,每一个防火墙可以有多个table;第二级是hook级,每一个table都有一个hook集合,每个hook都有一个防火墙规则链;第三级基本规则级,基本规则级的规则包括三部分,IP规则信息、匹配规则信息和target。而这三个组成部分的每一个都可以包括同类型的多个部分规则。
    433 extern int ipt_register_table(struct ipt_table *table);
    434 extern void ipt_unregister_table(struct ipt_table *table);
    435 extern unsigned int ipt_do_table(struct sk_buff **pskb,
    436                                  unsigned int hook,
    437                                  const struct net_device *in,
    438                                  const struct net_device *out,
    439                                  struct ipt_table *table,
    440                                  void *userdata);
    441
    有关table结构对齐的宏。

    442 #define IPT_ALIGN(s) (((s) + (__alignof__(struct ipt_entry)-1))  ~(__alignof__(struct ipt_entry)-1))
    443 #endif /*__KERNEL__*/
    444 #endif /* _IPTABLES_H */
    445


    西安交通大学  王灏

    防火墙技术分析讲义


    防火墙技术分析讲义
                                            yawl@docshow.net

    一 基本概念

    1.1 防火墙分类:
            包过滤
            代理(应用层网关)

    1.2 代理:

            两个连接(browser与proxy之间,proxy与web server之间)。
            工作在应用层。

            直接发往服务器的包:
            GET / HTTP/1.1
            Accept:        */*
            Accept-Language: zh-cn
            Accept-Encoding: gzip, deflate
            User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
            Host: www.lisoleg.net
            Connection: Keep-Alive

            往代理发出的包:
            GET http://www.lisoleg.net/ HTTP/1.1
            Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-powerpoint, application/msword, */*
            Accept-Language: zh-cn
            Accept-Encoding: gzip, deflate
            If-Modified-Since: Thu, 14 Dec 2000 07:24:52 GMT
            If-None-Match: "8026-185-3a3875c4"
            User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
            Host: www.lisoleg.net
            Proxy-Connection: Keep-Alive

            增强:
            cache

    1.3 包过滤
            单IP包检测
            缺陷:无状态

    1.4 增强1-状态检测(Stateful Inspection),又称动态包过滤(dynamic packet filtering)
            1.4.1 规则表和动态状态表

            1.4.2 ftp的例子:
            A 4847->B 21 PORT 192,168,7,60,18,241
            B 21->A 4847 PORT command successful.

            B 20->A 4849 syn
            > A classic example is transferring files using FTP. The firewall remembers the details of the
            > incoming request to get a file from an FTP server. The firewall then tracks the back-channel
            > request (the FTP Port command) by the server for transferring information back to the client.
            > As long as the information agrees (same IP addresses, no changes in port numbers, and no
            > non-FTP requests), the firewall allows the traffic. After the transfer is complete, the
            > firewall closes the ports involved.

            1.4.3 两种实现方法:
            1.4.3.1 checkpoint FW1,netfilter
            1.4.3.2 动态添加规则(ipchains patch)
            > I believe it does exactly what I want: Installing a temporary
                   > "backward"-rule to let packets in as a response to an
                   > outgoing request.


    1.5 增强2-地址转换:
            1.5.1 静态NAT
            1.5.2 动态NAT
            1.5.3 地址伪装


    1.6 增强3-VPN:
            位置的优越性

    二 Linux下防火墙的实现之一(2.2内核):

    2.1 截获位置:
            网络层

                      ----------------------------------------------------------------
                      |            ACCEPT/                              lo interface |
                      v           REDIRECT                  _______                  |
              --> C --> S --> ______ --> D --> ~~~~~~~~ -->|forward|----> _______ -->
                  h     a    |input |    e    {Routing }   |Chain  |     |output |ACCEPT
                  e     n    |Chain |    m    {Decision}   |_______| --->|Chain  |
                  c     i    |______|    a     ~~~~~~~~        |     | ->|_______|
                  k     t       |        s       |             |     | |     |
                  s     y       |        q       |             v     | |     |
                  u     |       v        e       v            DENY/  | |     v
                  m     |     DENY/      r   Local Process   REJECT  | |   DENY/
                  |     v    REJECT      a       |                   | |  REJECT
                  |   DENY               d       --------------------- |
                  v                      e -----------------------------
                 DENY


    2.2 提炼出的代码:

    输入检测:
            /*
            *         Main IP Receive routine.
            */
            int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
            {
            #ifdef        CONFIG_FIREWALL
                    int fwres;
                    u16 rport;
            #endif /* CONFIG_FIREWALL */

            ......

            #ifdef CONFIG_FIREWALL
                    /*
                    *        See if the firewall wants to dispose of the packet.
                    *
                    * We can't do ICMP reply or local delivery before routing,
                    * so we delay those decisions until after route. --RR
                    */
                    fwres = call_in_firewall(PF_INET, dev, iph, &rport, &skb);
                    if (fwres < FW_ACCEPT && fwres != FW_REJECT)
                            goto drop;
                    iph = skb->nh.iph;
            #endif /* CONFIG_FIREWALL */

            ......

            #ifdef CONFIG_FIREWALL
                    if (fwres == FW_REJECT) {
                            icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
                            goto drop;
                    }
            #endif /* CONFIG_FIREWALL */

                    return skb->dst->input(skb); //根据路由查找的结果决定是转发(ip_forward)还是发往上层(ip_local_deliver)
            drop:
                    kfree_skb(skb);        //如果规则匹配的结果是FW_REJECT,FW_BLOCK,丢弃此包
                    return(0);

            }

    转发检测:
            int ip_forward(struct sk_buff *skb)
            {
            ...
            #ifdef CONFIG_FIREWALL
                            fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb);
                            switch (fw_res) {
                            case FW_ACCEPT:
                            case FW_MASQUERADE:
                                    break;
                            case FW_REJECT:
                                    icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
                                    /* fall thru */
                            default:
                                    kfree_skb(skb);
                                    return -1;
                            }
            #endif
            ...
            }

    输出检测:(不同的上层协议走不同的流程,因此检测点较多)
            UDP/RAW/ICMP报文:ip_build_xmit
            TCP报文:ip_queue_xmit
            转发的包:ip_forward
            其他:ip_build_and_send_pkt


    实际的匹配:
            /*
            *        Returns one of the generic firewall policies, like FW_ACCEPT.
            *
            *        The testing is either false for normal firewall mode or true for
            *        user checking mode (counters are not updated, TOS & mark not done).
            */
            static int
            ip_fw_check(struct iphdr *ip,                 //IP头位置
                        const char *rif,                //出口网卡的名字
                        __u16 *redirport,                //端口转发时用到
                        struct ip_chain *chain,        //规则链的名字
                        struct sk_buff *skb,        //要检测的数据包
                        unsigned int slot,
                        int testing)                //见函数本身的注释

    调用举例:
            call_in_firewall实际调用ipfw_input_check,而ipfw_input_check中有:
            int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev,
                                 void *phdr, void *arg, struct sk_buff **pskb)
            {
                    return ip_fw_check(phdr, dev->name,
                                       arg, IP_FW_INPUT_CHAIN, *pskb, SLOT_NUMBER(), 0);
            }

    实际流程:

            ip_fw_check
            {
                    从传入的skb参数中提取源地址src,目的地址dst,源端口src_port,目的端口dst_port,
                    TCP发起连接标志tcpsyn,分片包位移offset,IP包TOS消息oldtos;

                    ......

                    f = chain->chain;        //取出规则链的的一条规则,规则链由chain参数传入
                    count = 0;
                    do {
                            for (; f; f = f->next) {        //遍历规则链中的规则,直到匹配(ip_rule_match返回1)
                                    count++;
                                    if (ip_rule_match(f,rif,ip,
                                                      tcpsyn,src_port,dst_port,offset)) {
                                            if (!testing
                                                && !ip_fw_domatch(f, ip, rif, chain->label,//作些标记,一般返回1
                                                                  skb, slot,
                                                                  src_port, dst_port,
                                                                  count, tcpsyn)) {
                                                    ret = FW_BLOCK;
                                                    goto out;
                                            }
                                            break;
                                    }
                            }
                            if(f) { //找到匹配规则

                                    ......

                            }else { //这次遍历根本没找到
                                    是从别的地方跳转过来的,则转回去,然后继续遍历;
                                    否则应用这条链的缺省规则;
                            }

                    } while (ret == FW_SKIP+2);
            out:

                    ......

                    return ret;
            }


    碎片:
            根据第一个片的消息进行过滤,其他分片则允许通过。如果规则是丢弃的话,虽然后面的分片都可到达主机,
            但由于第一片被滤掉了,无法重组成功,因此从效果上也相当于整个IP包被丢弃。

            存在的漏洞等.

    2.3 规则:

            from 192.168.7.0/24 to 192.168.6.32/32 tcp 80 BLOCK

    规则的数据结构表示:


            规则链
            struct ip_chain
            {
                    ip_chainlabel label;            /* Defines the label for each block */
                    struct ip_chain *next;            /* Pointer to next block */
                    struct ip_fwkernel *chain;  /* Pointer to first rule in block */
                    __u32 refcount;             /* Number of refernces to block */
                    int policy;                    /* Default rule for chain.  Only *
                                                 * used in built in chains */
                    struct ip_reent reent[0];   /* Actually several of these */
            };

            规则
            struct ip_fwkernel
            {
                    struct ip_fw ipfw;
                    struct ip_fwkernel *next;        /* where to go next if current
                                                    * rule doesn't match */
                    struct ip_chain *branch;        /* which branch to jump to if
                                                    * current rule matches */
                    int simplebranch;                /* Use this if branch == NULL */
                    struct ip_counters counters[0]; /* Actually several of these */
            };

            待匹配的数据包消息
            struct ip_fw
            {
                    struct in_addr fw_src, fw_dst;                /* Source and destination IP addr */
                    struct in_addr fw_smsk, fw_dmsk;        /* Mask for src and dest IP addr */
                    __u32 fw_mark;                          /* ID to stamp on packet */
                    __u16 fw_proto;                         /* Protocol, 0 = ANY */
                    __u16 fw_flg;                                /* Flags word */
                    __u16 fw_invflg;                        /* Inverse flags */
                    __u16 fw_spts[2];                       /* Source port range. */
                    __u16 fw_dpts[2];                       /* Destination port range. */
                    __u16 fw_redirpt;                       /* Port to redirect to. */
                    __u16 fw_outputsize;                    /* Max amount to output to
                                                               NETLINK */
                    char           fw_vianame[IFNAMSIZ];        /* name of interface "via" */
                    __u8           fw_tosand, fw_tosxor;        /* Revised packet priority */
            };


    2.4 地址转换
            ip_fw_demasquerade
            ip_fw_masquerade


    三 Linux下防火墙的实现之二(2.4内核):
    3.1
            A Packet Traversing the Netfilter System:

            --->PRE------>[ROUTE]--->FWD---------->POST------>
                   Conntrack    |       Filter   ^    NAT (Src)
                   Mangle       |                |    Conntrack
                   NAT (Dst)    |             [ROUTE]
                   (QDisc)      v                |
                                IN Filter       OUT Conntrack
                                |  Conntrack     ^  Mangle
                                |                |  NAT (Dst)
                                v                |  Filter


    3.2 例子

    ## Insert connection-tracking modules (not needed if built into kernel).
    # insmod ip_conntrack
    # insmod ip_conntrack_ftp

    ## Create chain which blocks new connections, except if coming from inside.
    # iptables -N block
    # iptables -A block -m state --state ESTABLISHED,RELATED -j ACCEPT
    # iptables -A block -m state --state NEW -i ! ppp0 -j ACCEPT
    # iptables -A block -j DROP

    ## Jump to that chain from INPUT and FORWARD chains.
    # iptables -A INPUT -j block
    # iptables -A FORWARD -j block

    3.3 规则的描述
            一条规则分为三部分:
            struct ipt_entry        //主要用来匹配IP头
            struct ip_match        //额外的匹配(tcp头,mac地址等)
            struct ip_target        //除缺省的动作外(如ACCEPT,DROP),可以增加新的(如REJECT)。

    3.4 代码提炼

    ip_input.c:
            /*
            *         Main IP Receive routine.
            */
            int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
            {
            ...
            return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
                                   ip_rcv_finish);
            ...
            }

    netfilter.h:
            #ifdef CONFIG_NETFILTER
            #define NF_HOOK(pf, hook, skb, indev, outdev, okfn)                        \
            (list_empty(&nf_hooks[(pf)][(hook)])                                        \
            ? (okfn)(skb)                                                                \
            : nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
            #else /* !CONFIG_NETFILTER */
            #define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
            #endif /*CONFIG_NETFILTER*/

    大的框架:"HOOK表":
            struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];        //netfilter.c
            通过nf_register_hook和nf_unregister_hook完成添加删除工作,nf_iterate负责执行hook上的函数。

            增加用户自定义的HOOK,参见【8】,【10】:

    重要流程(建议结合netfilter hacking howto 4.1.3来看):
    /* Returns one of the generic firewall policies, like NF_ACCEPT. */
    unsigned int
    ipt_do_table(struct sk_buff **pskb,
                 unsigned int hook,
                 const struct net_device *in,
                 const struct net_device *out,
                 struct ipt_table *table,
                 void *userdata)
    {
            struct ipt_entry *e;
            struct ipt_entry_target *t;
            unsigned int verdict = NF_DROP;

            table_base = (void *)table->private->entries
                    + TABLE_OFFSET(table->private,
                                   cpu_number_map(smp_processor_id()));
            e = get_entry(table_base, table->private->hook_entry[hook]);

            ...
            ip_packet_match(ip, indev, outdev, &e->ip, offset);

            ...
            IPT_MATCH_ITERATE(e, do_match, *pskb, in, out, offset, protohdr, datalen, &hotdrop)

            ...
            t = ipt_get_target(e);

            ...
            verdict = t->u.kernel.target->target(pskb, hook, in, out, t->data, userdata);//非标准的target走这一步

            ...
            return verdict;
    }

    要加强对这段话的理解(netfilter hacking howto 4.1节) :
    >iptables does not register with any netfilter hooks: it relies on
    >other modules to do that and feed it the packets as appropriate; a
    >module must register the netfilter hooks and ip_tables separately, and
    >provide the mechanism to call ip_tables when the hook is reached.

    四 Linux下防火墙的实现之三(checkpoint FW1)

    让我们看看checkpoint的在linux上的防火墙是如何实现的,最终我们会发现,竟然和lkm使用的手段差不多:)

    fw1通过dev_add_pack的办法加载输入过滤函数,但是在net_bh()中,传往网络层的skbuff是克隆的,即
    skb2=skb_clone(skb, GFP_ATOMIC);
    if(skb2)
            pt_prev->func(skb2, skb->dev, pt_prev);
    而fw1是怎么解决这个问题的呢?见下面的代码:

    输入一:

            ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
                            align 4

            ; 圹圹圹圹圹圹圹?S U B        R O U T        I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?

            ; Attributes: bp-based frame

                            public fwinstallin
            fwinstallin        proc near                ; CODE XREF: fwinstall+E9p
                                                    ; fwinstall+149p

            var_18                = byte ptr -18h
            arg_0                = dword        ptr  8

                            push        ebp
                            mov        ebp, esp
                            sub        esp, 10h
                            push        esi
                            push        ebx
                            mov        esi, ds:dev_base
                            cmp        [ebp+arg_0], 0
                            jz        short loc_0_802CBD0
                            add        esp, 0FFFFFFF4h
                            push        offset fw_ip_packet_type
                            call        dev_add_pack
                            mov        ebx, fw_ip_packet_type+10h        ;如果考虑字节对齐问题的话fw_ip_packet_type+10h这时应该是ip_packet_type
                            mov        dword ptr ds:fw_type_list, ebx
                            jmp        short loc_0_802CB9C
            ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
                            align 4

            loc_0_802CB90:                                ; CODE XREF: fwinstallin+41j
                            add        esp, 0FFFFFFF4h
                            push        ebx
                            call        dev_remove_pack                ;fw1把ip_packet_type歇载掉了,然后自己在自己的处理函数(fw_filterin)中调ip_recv
                            mov        ebx, [ebx+10h]

            loc_0_802CB9C:                                ; CODE XREF: fwinstallin+2Dj
                            add        esp, 10h
                            test        ebx, ebx
                            jnz        short loc_0_802CB90
                            test        esi, esi
                            jz        short loc_0_802CC14

            loc_0_802CBA7:                                ; CODE XREF: fwinstallin+68j
                            test        byte ptr fwdebug, 81h
                            jz        short loc_0_802CBC3
                            add        esp, 0FFFFFFF8h
                            mov        eax, [esi]
                            push        eax
                            push        offset aFwinstallinS ; "fwinstallin: %s\n"
                            call        fwkdebug_printf
                            add        esp, 10h

            loc_0_802CBC3:                                ; CODE XREF: fwinstallin+4Ej
                            mov        esi, [esi+28h]
                            test        esi, esi
                            jnz        short loc_0_802CBA7
                            jmp        short loc_0_802CC14
            ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
                            align 8

            loc_0_802CBD0:                                ; CODE XREF: fwinstallin+12j
                            cmp        dword ptr ds:fw_type_list, 0
                            jz        short loc_0_802CC14
                            add        esp, 0FFFFFFF4h
                            push        offset fw_ip_packet_type
                            call        dev_remove_pack
                            add        esp, 10h
                            cmp        dword ptr ds:fw_type_list, 0
                            jz        short loc_0_802CC14

            loc_0_802CBF2:                                ; CODE XREF: fwinstallin+B2j
                            add        esp, 0FFFFFFF4h
                            mov        eax, dword ptr ds:fw_type_list
                            push        eax
                            call        dev_add_pack
                            mov        eax, dword ptr ds:fw_type_list
                            add        esp, 10h
                            mov        eax, [eax+10h]
                            mov        dword ptr ds:fw_type_list, eax
                            test        eax, eax
                            jnz        short loc_0_802CBF2

            loc_0_802CC14:                                ; CODE XREF: fwinstallin+45j
                                                    ; fwinstallin+6Aj ...
                            lea        esp, [ebp+var_18]
                            xor        eax, eax
                            pop        ebx
                            pop        esi
                            mov        esp, ebp
                            pop        ebp
                            retn
            fwinstallin        endp

    输入二:
            public fw_ip_packet_type
            fw_ip_packet_type dd 8,        0, offset fw_filterin, 2 dup(0)        ; DATA XREF: fwinstallin+17o


    输出的挂载和lkm的手法一样,更改dev->hard_start_xmit。dev结构在2.2版本的发展过程中变了一次,
    为了兼容fw1对这点也做了处理。


    输出一:
            ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
                            align 4

            ; 圹圹圹圹圹圹圹?S U B        R O U T        I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?

            ; Attributes: bp-based frame

                            public fwinstallout
            fwinstallout        proc near                ; CODE XREF: fwinstall+FBp
                                                    ; fwinstall+153p

            var_18                = byte ptr -18h
            arg_0                = dword        ptr  8

                            push        ebp
                            mov        ebp, esp
                            sub        esp, 0Ch
                            push        edi
                            push        esi
                            push        ebx
                            mov        edi, [ebp+arg_0]
                            xor        esi, esi
                            mov        ebx, ds:dev_base
                            jmp        short loc_0_802D0A8
            ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?

            loc_0_802D096:                                ; CODE XREF: fwinstallout+50j
                            add        esp, 0FFFFFFFCh
                            push        edi
                            push        esi
                            push        ebx
                            call        installout_on_device
                            add        esp, 10h
                            mov        ebx, [ebx+28h]
                            inc        esi

            loc_0_802D0A8:                                ; CODE XREF: fwinstallout+14j
                            test        ebx, ebx
                 &n