zoukankan      html  css  js  c++  java
  • Linux防火墙,Netfiler,iptables概念原理全解析(上)

    前言:  

           不知道你有没有这样的困惑,iptables会用,可总是知其然不知其所以然,然后常常江里面的概念搞混,尤其是类似的操作,却常常是以不同的称谓出现:netfilter,iptables, firewalld....

    所以,我们有必要了解一下其真正的内核实现,这样有助于我们记忆iptables的用法。

            本文主要是基于官网文档进行的翻译以及自己的理解: https://netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO.html#toc3,     还包括一些其他的连接都会在相应的位置注明

    大纲:

    1.netfilter框架

       1.1 netfilter原理概述

       1.2  netfilter的钩子们

       1.3 netfilter的核心基础

       1.4 代码摘要

       1.5 小小结

    2.基于netfilter框架的实现

       2.1 数据包筛选: IP Tables

       2.2 连接跟踪(Connection Tracking)

       2.3 其他待补充

       2.4 小小结

     附: IP Tables的代码实现举例&实操

    3.IP Tables的内核空间与用户空间

       3.1 ip_tables的数据结构

       3.2 用户空间的ip_tables

       3.3  ip_tables的使用和遍历

       3.4  用户空间工具

       3.5  共享库: libiptc

       3.6  小小结

       附:实操

    4. iptables, iptables.service, firewalld.service,firewall-cmd辨析

       4.1 iptables.service 和 iptables cmd

       4.2 由firewalld实现的动态防火墙

       4.3 firewalld.service和iptables.service

       4.4 小小结

     

    一. netfilter框架


    1. netfilter原理概述

    是一个脱离于普通socker接口的数据包处理框架(framework)。包含四个部分,

    首先:hooks的定义.
       每种协议都定义了一些hooks即钩子(IPV4共定义的是5个),所谓的hooks是指在协议栈做包遍历时,一些被明确定义的点(well-defined points)
         于是在每一个point上,该协议栈都会使用数据包和hook号去调用netfilter框架。

    其次:内核注册监听.
       内核的某些部分可以针对每个协议的不同hook注册监听,当数据包被传递到netfilter框架(的hook)时,就会检查是否有针对这个协议的这个hook注册监听;
       如果注册了,这些数据包就将面临依序的检查甚至可能是被更改(a chance to examine and possibly alter ),然后决定接下来是销毁(NF_DROP), 还是允许(NF_ACCEPT), 还是告诉netfilter忽略(NF_STOLEN), 或者要求netfilter将这些数据包1进行排队以用于用户空间(NF_QUEUE);

    第三部分:处理结果
         将已经排队的数据包收集起来(由ip_queue驱动),然后发送到用户空间,为异步处理。

    第四部分:文档和注释.....

    在原生的netfilter框架之外,是各种各样的模块, 其中较为重要的是可扩展的NAT系统和数据包过滤系统(iptables)

    2. netfilter的钩子们

    根据前面的概述可以看出来,netfilter框架的核心其实就是一系列的钩子(hooks), 接下来我们就以IPV4为例,看看理想情况下这些hooks都放在了哪里?

      A Packet Traversing the Netfilter System:

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

    解析:
     1)对于从外面接收过来的数据包, 首先会经历框架的第1号hook, 称为NF_IP_PRE_ROUTING,通过后进入路由的逻辑;
      在路由逻辑中, 会根据包的目的地址进行分流,
      如果目的地址是自己, 那么会进入第2号hook,称为NF_IP_LOCAL_IN,处理完才会交给上层进程
      如果目的地不是字节,即需要交给另一个interface转发出去的话,那在转发之前进入第3号hook, 称为NF_IP_FORWARD,
      最后,交给第4号hook,称为NF_IP_POST_ROUTING, 之后就可以发送出去了。
      2) 对于本地生成的数据包, 在进入路由逻辑之前会进入第5号hook, 称为NF_IP_LOCAL_OUT。
      注:实际上在这种场景下, 第一次调用路由代码是发生在hook之前,因为需要为报文填充源IP和一些IP选项。

     

    3. netfilter的核心基础

    原文:Kernel modules can register to listen at any of these hooks. A module that registers a function must specify the priority of the function within the hook;
    解析:
      钩子的位置规定好了,接下来就是去钩子那里注册一下。这些去注册的内核模块通过实现相应的注册函数在hook里面放置一个监听,注册函数必须有优先级才行,只有这样当netfilter hook在被内核网络代码调用的时候,会按序执行这些模块所注册的函数, 执行完毕了,这些模块还有告诉netfilter接下来做什么,且是如下五种之一
    1)NF_ACCEPT: 接下来继续遍历吧.
    2)NF_DROP: 丢弃数据包; 不用继续遍历了.
    3)NF_STOLEN: 数据包我接收了;不用继续遍历了.
    4)NF_QUEUE: 将数据包入列 (通常用于用户态处理).
    5)NF_REPEAT: 再次调用该hook.

    在此基础上,我们可以构建相当复杂的数据包操作,比如如下章节所示。

     

     

    4.代码摘要

    • hooks的定义,由具体的协议栈决定,以ipv4协议栈为例:
      • 源码目录: includeuapilinux
        etfilter.h
        enum nf_inet_hooks {
            NF_INET_PRE_ROUTING,
            NF_INET_LOCAL_IN,
            NF_INET_FORWARD,
            NF_INET_LOCAL_OUT,
            NF_INET_POST_ROUTING,
            NF_INET_NUMHOOKS,
            NF_INET_INGRESS = NF_INET_NUMHOOKS,
        };
    • 定义注册监听的接口(注:表示框架即netfiler只是定义了注册接口,具体注册由外部模块主动发起)
      • 目录:linux-3.10.1
        et
        etfiltercore.c,
        核心代码:
          0). 定义注册参数类型
            struct nf_hook_ops {
              struct list_head list;
        
              /* User fills in from here down. */
              nf_hookfn *hook;       //1.表示的含义是用户注册的回调函数, 即到达netfiler中的hook时, 需要执行的具体方法
              struct module *owner;
              u_int8_t pf;
              unsigned int hooknum;  //2. hook的编号, 即表示注册到哪个hook上, 表如可以是NF_IP_PRE_ROUTING
              /* Hooks are ordered in ascending priority. */
              int priority;          //3. 优先级
          };
          1). 注册接口函数: 提供一个接口用于其他组件向hook中注册回调函数
            int nf_register_hook(struct nf_hook_ops *reg) {
              //1. 根据优先级找到(最大的那个)比我低的
              list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
                  if (reg->priority < elem->priority)
                      break;
              }
              //2. 然后将我自己添加到链表中,位置也就是刚好找到的那个最大比我低的那个
              list_add_rcu(&reg->list, elem->list.prev);
          }
          2). 具体的注册, 见下一个章节
    • 综合:将netfiler框架和协议栈结合
      • 目录:linux-3.10.1
        etipv4ip_input.c
        //说明:协议栈接收到数据后,需要借助netfiler框架进入到hooks中,然后进一步进入到注册的处理函数中
        int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { ... //进入netfiler框架:会调用nf_hooks_active(pf, hook)函数,具体的操作包括: // 会调用注册在NF_INET_PRE_ROUTING这个hook中的所有函数, // ip_rcv_finish return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, ip_rcv_finish); }

    一些参考链接:https://blog.csdn.net/lickylin/article/details/33020731

    5. 小小结

    netfilter顾名思义,是一个用来做网络过滤用的.他的实现只是一个框架,也就是说他只是定义了一套怎样做过滤的体系,
    那么,怎么做过滤呢? 这个过程涉及了三个方面:负责收发数据包的协议栈,netfiler,有过滤需求的模块,
    其中,netfiler则作为核心框架将协议栈和模块进行有机结合,整个过程具体来说就是:
      首先,框架允许协议栈定义几个安插点,称为hook/钩子,表示协议栈在处理数据流中,数据包会在这几个安插点上面临过滤;
      然后,框架支持模块向hook中进行注册(函数),表示数据流具体面临的具体过滤操作是什么;
      最后,当协议栈真正接收到数据后,会进入netfiler框架,之后数据包将在对应的hook按序被注册的函数处理。

    wxy:

    关于hook和hook函数:
    hook是具体的协议栈定义的, 即所谓的"well-defined points",可以看做是"打点"
    hook函数是具体的模块注册的,即所谓的"examine and possibly alter", 可以看做是"点上的回调"

    二. 基于netfilter框架的实现


    netfiler是一个框架,他的工作只是负责:
    对上(协议栈),允许协议栈定制hook,并指导数据包到达协议栈某个hook对应的point后进入netfiler框架中;
    对下(模块), 支持注册回调函数, 并指导接收到数据包会依次被回调函数处理.
    所以,这里一个关键的环节就是:谁去注册? 如何注册?以及他注册了哪些处理行为(回调函数)。
    于是,一些内置的系统比如IP Tables,Connection Tracking则帮等我们实现了这些注册,并且有的还提供了面向用户的操作。

    1. 数据包筛选: IP Tables

    数据包的筛选系统被称为IP Tables,其已经被构建在netfilter框架中了. 他托生于ipchains(来自ipfwadm,来自BSD的ipfw IIRC) ,并具有可扩展行。内核模块可以注册一个新表,然后要求数据包去遍历这个给定的表(Kernel modules can register a new table, and ask for a packet to traverse a given table)。这种对包进行筛选的方法则是用来进行包过滤( "filter"表),网络地址转化('nat'表),常规的路由前数据包处理('mangle'表)

      每一个hook点都注册了哪些表,以及优先级是怎样的,如下图所示:

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

    wxy: 
    关于注册,netfilter部分的描述是:Kernel modules can register to listen at any of these hooks. A module that registers a function...  而此时就变成了: Kernel modules can register a
    new table, and ask for a packet to traverse a given table. 也就是说此时 表(table) 变成了注册的内容,该怎么理解呢?结合Ip tables部分的源码可以知道,其实是这样的: 首先,内核模块们都可以(不确定是不是任何的模块)在hook点注册自己的回调函数,回调中具体做什么也是模块自己的事情 然后,IP Tables系统/模块,是一种内置的实现, 即帮我们在hook上注册了回调,该回调的内部实现中,对数据的操作机制是借助一个可定制的"规则表"
    即回调函数就可以根据该"规则表"对数据包进行处理。而内核中的其他模块,都可以想自己的需求注册到"规则表"甚至新建"规则表"; 综上所述,可以认为是IP Tables用来协助内核模块去hook上注册函数.....

    解析:
    1). Filter 包过滤, filter表
     filter表永远不会修改包,只会过滤他们;filter表都注册到哪些hook中呢?有3/5个hook points都这个表的身影,分别是:
        NF_IP_LOCAL_IN: 对于目的是自己的报文,在进入本地处理之前
        NF_IP_FORWARD:对于目的是别人,在转发之前
        NF_IP_LOCAL_OUT: 对于自己产的,在发出去之前
        以上三点可以看出, filter表的安插让每一个数据包,有且只有一次需要经历filter的过滤。

    2). Nat地址转换, nat表
        NF_IP_PRE_ROUTING 和 NF_IP_POST_ROUTING: 针对的是 non-local, 即目的不是自己的数据包,分别在入口和出口处做"目的转换"和"源转换"。
        NF_IP_LOCAL_OUT 和 NF_IP_LOCAL_IN: 如果定义了CONFIG_IP_NF_NAT_LOCAL, 则用于altering the destination of local packets.
      (wxy: 其中NF_IP_LOCAL_IN据说从Centos7才有,6没有。 另外均是做目的转换么??)

    3). Mangle,mangle表
        用于拆解数据包然后更改信息,例如用于TOS和TCPMSS。五个hook均注册了mangle表。

    2. 连接跟踪(Connection Tracking)

    原文:Connection tracking is fundamental to NAT, but it is implemented as a separate module; this allows an extension to the packet filtering code to simply and cleanly use connection tracking (the `state' module).
    解析:

    实现原理(待整理)
    1. nf_conntrack的原理
    参考链接:
    https://blog.csdn.net/dog250/article/details/78372576
    connection tracking, 即连接跟踪,用来保存连接信息的,在进入hook之前就会进行的逻辑处理,每一个连接对应的结构体为:
    连接跟踪是 在PREROUTING链里进行处理的,更确切的说是在该链之前,只不过二者通常是绑定出现?
    conntrack默认最大跟踪65536个连接,查看当前系统设置最大连接数:
    cat /proc/sys/net/netfilter/nf_conntrack_max

    查看连接跟踪有多少条目:
    # cat /proc/sys/net/netfilter/nf_conntrack_count

    查看established连接状态最多保留几天,默认是432000秒,就是5天;如果觉得时间太长可以修改。还有各种tcp连接状态的保留时间,都可以修改的。
    # cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established

    IP_conntrack模块根据IP地址可以实时追踪本机TCP/UDP/ICMP的连接详细信息并保存在内存中“/proc/net/nf_conntrack”文件中
    例如我的82环境:
    [root@tmp-82 net]# cat /proc/sys/net/netfilter/nf_conntrack_max
    65536
    [root@tmp-82 net]# cat /proc/sys/net/netfilter/nf_conntrack_count
    13
    [root@tmp-82 net]# cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
    432000

    [root@tmp-82 net]# cat /proc/net/nf_conntrack
    ipv4 2 tcp 6 431978 ESTABLISHED src=192.168.48.231 dst=192.168.48.82 sport=35356 dport=23002 src=192.168.48.82 dst=192.168.48.231 sport=23002 dport=35356 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 udp 17 29 src=192.168.48.248 dst=192.168.48.255 sport=57133 dport=18000 [UNREPLIED] src=192.168.48.255 dst=192.168.48.248 sport=18000 dport=57133 mark=0 zone=0 use=2
    ipv4 2 unknown 2 599 src=192.168.48.2 dst=224.0.0.1 [UNREPLIED] src=224.0.0.1 dst=192.168.48.2 mark=0 zone=0 use=2
    ipv4 2 tcp 6 431996 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=60446 dport=23002 src=192.168.48.82 dst=192.168.48.159 sport=23002 dport=60446 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431992 ESTABLISHED src=192.168.48.224 dst=192.168.48.82 sport=59644 dport=23002 src=192.168.48.82 dst=192.168.48.224 sport=23002 dport=59644 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431995 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=60444 dport=23002 src=192.168.48.82 dst=192.168.48.159 sport=23002 dport=60444 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431984 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=60415 dport=23002 src=192.168.48.82 dst=192.168.48.159 sport=23002 dport=60415 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431995 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=33918 dport=22 src=192.168.48.82 dst=192.168.48.159 sport=22 dport=33918 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431984 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=60414 dport=23002 src=192.168.48.82 dst=192.168.48.159 sport=23002 dport=60414 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 299 ESTABLISHED src=192.168.95.143 dst=192.168.48.82 sport=56724 dport=22 src=192.168.48.82 dst=192.168.95.143 sport=22 dport=56724 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431974 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=52380 dport=23002 src=192.168.48.82 dst=192.168.48.159 sport=23002 dport=52380 [ASSURED] mark=0 zone=0 use=2
    ipv4 2 tcp 6 431974 ESTABLISHED src=192.168.48.159 dst=192.168.48.82 sport=52378 dport=23002 src=192.168.48.82 dst=192.168.48.159 sport=23002 dport=52378 [ASSURED] mark=0 zone=0 use=2
    View Code

    在一个由容器的宿主机上查看关于容器的条目
    [root@node231 ~]# cat /proc/net/nf_conntrack|grep 23002
    ipv4 2 tcp 6 86397 ESTABLISHED src=172.17.0.2 dst=192.168.48.82 sport=35356 dport=23002 src=192.168.48.82 dst=192.168.48.231 sport=23002 dport=35356 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2


    字段说明:
    ASSURED: 说明两个方向已没有流量


    wxy: CentOS Linux release 7.7.1908 (Core) 发下如上的命令不好用
    CentOS Linux release 7.3.1611 (Core)是ok, 不知道是版本问题还是因为前者没有连接信息

     

    3. 其他待补充

     

    4. 小小结

    在这里,先不要考虑我们经常使用的那个iptables命令(后面会讲他),而是将其看做是一个内置功能,为了区别将其称之为IP Tables。
    前面说了,既然netfiler给我们提供hook定义 + 回调函数注册hook + 具体实现回调这样一整套的机制;
    针对各个协议,hook的定义已经做好比如ipv4(前面的章节),所以接下来的实现就由这样一个已经内置的系统,称之为: IP Tables
    他实现了这样一个功能:
      1)内存中维护了一个表,里面是一条一条规则
      2)去netfilter中那里注册hook(即,回调函数),该函数会根据表中的规则对数据进行操作(filter, nat, mangle,raw)

    关于链chain的含义:因为在netfilter的每个hook点上,可以注册多个表/操作表的hook函数, 且具有一定的顺序,于是他们就构成了一个链.

    根据源码的位置也可以发现netfilter和 IP Tables之间的关系
    netfilter:net etfilter, 网络中的一个模块
    IP Tables:netipv4 etfilter, 网络中ipv4数据包中,关于netfilter方面的内容

    wxy:
    目前我对这个所谓的系统的理解是,他只是一个内核单独的功能模块,如果想用这个功能则需要加载才行

    附1: IP Tables的代码实现举例

    IP Tables系统针对多个协议都实现了 回调函数注册hook +  回调函数实现 :ipv4/ipv6协议栈,arp...(wxy:  还有其他?  待补充)

    • ipv4
      • 目录:linux-3.10.1
        etipv4
        etfilterip_tables.c,iptable_filter.c,iptable_nat.c,iptable_mangle.c,.....
        核心实现:
        0). ip_tables初始
             module_init(ip_tables_init);
             static int __init ip_tables_init(void)
             {    
            //part1: 向下协议栈
            ret = register_pernet_subsys(&ip_tables_net_ops);
        
            //part2: 自身:
            ret = xt_register_targets(ipt_builtin_tg, ARRAY_SIZE(ipt_builtin_tg));
            ret = xt_register_matches(ipt_builtin_mt, ARRAY_SIZE(ipt_builtin_mt));
        
            //part3: 向上用户空间, socket注册, 用来监听用户空间的请求(wxy:很重要,libiptc库就是通过socket与之交互)
            /* Register setsockopt */
            ret = nf_register_sockopt(&ipt_sockopts);   //pf为: PF_INET,  操作类别:IPT_BASE_CTL ~ IPT_SO_SET_MAX
        
        
        }
            其中,iptable_filter_net_ops  -> ip_tables_net_init  ->  xt_proto_init
             {
                
              }
        1). filter表
           module_init(iptable_filter_init);
           static int __init iptable_filter_init(void)
           {
            //part1: 会进行表注册, 即为网络实例net创建ipv4的过滤表
            ret = register_pernet_subsys(&iptable_filter_net_ops);
        
            //part2: hook的注册 /* Register hooks */ 
            filter_ops = xt_hook_link(&packet_filter, iptable_filter_hook);
                    { ... ret = nf_register_hooks(ops, num_hooks);  ...}
                    
            ...
           }
           其中, 
            1)
            static struct pernet_operations iptable_filter_net_ops = {
               .init = iptable_filter_net_init,
               .exit = iptable_filter_net_exit,
            };
            static int __net_init iptable_filter_net_init(struct net *net)
            {
               repl = ipt_alloc_initial_table(&packet_filter);   //1. 初始化对应的replace实例
               net->ipv4.iptable_filter =
                ipt_register_table(net, &packet_filter, repl); //2. 注册table
          }
        
          struct xt_table *ipt_register_table(struct net *net,
                            const struct xt_table *table, const struct ipt_replace *repl)
          {
               //1.
               newinfo = xt_alloc_table_info(repl->size);
        
               //2.
               ret = translate_table(net, newinfo, loc_cpu_entry, repl);
            
               //3. 
               new_table = xt_register_table(net, table, &bootstrap, newinfo);
          }
        
            
        2)static const struct xt_table packet_filter = {
             .name        = "filter",
              //这个表感兴趣的hook点,有三个: ((1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT))
             .valid_hooks    = FILTER_VALID_HOOKS,   
             .me        = THIS_MODULE,
             .af        = NFPROTO_IPV4,
             .priority    = NF_IP_PRI_FILTER,
          };
        
        3)static unsigned int iptable_filter_hook(unsigned int hook, struct sk_buff *skb,
                    const struct net_device *in, const struct net_device *out,
                    int (*okfn)(struct sk_buff *))
        {
            return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_filter);
        }
        
        2). nat表
        ...
        ...
        View Code
    • arp
      • module_init(arptable_filter_init);
        static int __init arptable_filter_init(void)
        {
            ret = register_pernet_subsys(&arptable_filter_net_ops);
            arpfilter_ops = xt_hook_link(&packet_filter, arptable_filter_hook);
        }
        View Code

    附2:模块关系示例

    # lsmod
    Module                   Size      Used by
    iptable_nat             12875      1 
    nf_nat_ipv4             14115      1 iptable_nat
    iptable_mangle      12695      1 
    iptable_security      12705      1 
    iptable_raw             12678      1 
    iptable_filter           12810      1 
    ip_tables                  27115      5 iptable_security,iptable_filter,iptable_mangle,iptable_nat,iptable_raw      //被这些功能模块所调用
    
    [root@host231 ~]# modinfo ip_tables
    filename:       /lib/modules/3.10.0-514.el7.x86_64/kernel/net/ipv4/netfilter/ip_tables.ko
    description:    IPv4 packet filter
    ...
    [root@host231 ~]# modinfo iptable_filter
    filename:       /lib/modules/3.10.0-514.el7.x86_64/kernel/net/ipv4/netfilter/iptable_filter.ko
    description:    iptables filter table
    depends:        ip_tables     //ip_tables这个模块加载后,才可以支撑iptable_filter模块的加载:1)表创建/注册; 2)hooks注册
    ...

    <续>: Linux防火墙,Netfiler,iptables概念原理全解析(下) - 水鬼子 - 博客园 (cnblogs.com)

  • 相关阅读:
    java se 转到ee小结
    objective c基本知识
    2013_11_14:递归算法(2)—全排列
    2013_11_13:递归算法(1)
    2013_11_13:关于 new 和delelte 的使用
    c++调试问题
    模式匹配BM算法介绍与实现
    一个n数组求和问题
    机试题
    算法题目
  • 原文地址:https://www.cnblogs.com/shuiguizi/p/15438069.html
Copyright © 2011-2022 走看看