• linux nf_conntrack 连接跟踪机制


    PRE_ROUTING和LOCAL_OUT点可以看作是整个netfilter的入口,而POST_ROUTING和LOCAL_IN可以看作是其出口;

    报文到本地PRE_ROUTING----LOCAL_IN---本地进程

    需要本机转发的数据包:PRE_ROUTING---FORWARD---POST_ROUTING---外出

    从本机发出的数据包:LOCAL_OUT----POST_ROUTING---外出

     数据包文到达内核协议栈时,使用sk_buff{}(即skb),其类型为struct nf_conntrack  *;该结构记录了连接记录被公开应用的计数,也方便其他地方对连接跟踪的引用;

    连接跟踪在实际应用中一般都通过强制类型转换将nfct转换成指向struct nf_conn { }类型;

    同时:skb->nfctinfo 记录了该数据包的连接状态和该连接状态的相关信息;nfctinfo表示了每个数据包的几种连接状态;

    Neftilter框架用struct   nf_conn{ }来记录一个数据包与其连接的状态关系;

    其中nfctinfo 取值有

    /* Connection state tracking for netfilter.  This is separated from,
       but required by, the NAT layer; it can also be used by an iptables
       extension. */
    enum ip_conntrack_info {
        /* Part of an established connection (either direction). 
          已建立连接的一部分(任一方向) */
        IP_CT_ESTABLISHED,
    
        /* Like NEW, but related to an existing connection, or ICMP error
           (in either direction). */
     /* 数据报属于一个新的连接 但此连接与一个现有连接相关(预期连接),或者是ICMP错误(任一方向) */
        IP_CT_RELATED,
    
        /* Started a new connection to track (only
               IP_CT_DIR_ORIGINAL); may be a retransmission. */
    /* 开始一个新连接; 可能是重传---  数据报属于一个新的连接 */
        IP_CT_NEW,
     /* >=这个值的都是响应方向的  数据报属于一个连接的回复*/
        /* >= this indicates reply direction */
        IP_CT_IS_REPLY,
    /* 已建立连接的响应 */
        IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
     /* 已建立连接的关联连接的响应 */
        IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
        /* No NEW in reply direction. */
     /* IP_CT类型的数量 */
        /* Number of distinct IP_CT types. */
        IP_CT_NUMBER,
    
        /* only for userspace compatibility */
    #ifndef __KERNEL__
        IP_CT_NEW_REPLY = IP_CT_NUMBER,
    #endif
    };        

    在连接跟踪内部,收到的每个skb首先被转换成一个nf_conntrack_tuple{}结构,也就是说nf_conntrack_tuple{}结构才是连接跟踪系统所“认识”的数据包 ;

    skb和ip_conntrack_tuple{}结构之间是如何转换的呢?

    对于TCP/UDP协议,根据“源、目的IP+源、目的端口”再加序列号就可以唯一的标识一个数据包了;对于ICMP协议,根据“源、目的IP+类型+代号”再加序列号才可以唯一确定一个ICMP报文等等

    nf_conntrack 数据结构

    /*
     struct sk_buff {
    
          struct nf_conntrack *nfct;//指向struct nf_conn实例
          ..............
    }; 
    */
    struct nf_conn {//每个struct nf_conn实例代表一个连接。每个skb都有一个指针,指向和它相关联的连接。
        /* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
         * plus 1 for any connection(s) we are `master' for
         *
         * Hint, SKB address this struct and refcnt via skb->nfct and
         * helpers nf_conntrack_get() and nf_conntrack_put().
         * Helper nf_ct_put() equals nf_conntrack_put() by dec refcnt,
         * beware nf_ct_get() is different and don't inc refcnt.
         */
        struct nf_conntrack ct_general; //对连接的引用计数
    
        spinlock_t    lock;
        u16        cpu;
    
        /* These are my tuples; original and reply */
        /* Connection tracking(链接跟踪)用来跟踪、记录每个链接的信息(目前仅支持IP协议的连接跟踪)。
                每个链接由“tuple”来唯一标识,这里的“tuple”对不同的协议会有不同的含义,例如对tcp,udp
                     来说就是五元组: (源IP,源端口,目的IP, 目的端口,协议号),对ICMP协议来说是: (源IP, 目
                的IP, id, type, code), 其中id,type与code都是icmp协议的信息。链接跟踪是防火墙实现状态检
                测的基础,很多功能都需要借助链接跟踪才能实现,例如NAT、快速转发、等等。*/
        struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];//正向和反向的连接元组信息。
    /* 这是一个位图,是一个状态域。在实际的使用中,它通常与一个枚举类型ip_conntrack_status(位于include/linux/netfilter_ipv4/ip_conntrack.h,Line33)进行位运算来判断连接的状态。其中主要的状态包括:
    
    IPS_EXPECTED(_BIT),表示一个预期的连接
    
    IPS_SEEN_REPLY(_BIT),表示一个双向的连接
    
    IPS_ASSURED(_BIT),表示这个连接即使发生超时也不能提早被删除
    
    IPS_CONFIRMED(_BIT),表示这个连接已经被确认(初始包已经发出) */
     /* 可以设置由enum ip_conntrack_status中描述的状态 */
        /* Have we seen traffic both ways yet? (bitset) */
        unsigned long status;//该连接的连接状态
    
        /* Timer function; drops refcnt when it goes off. */
        struct timer_list timeout; //连接垃圾回收定时器  连接跟踪的超时时间 
    
        possible_net_t ct_net;
    
        /* all members below initialized via memset */
        u8 __nfct_init_offset[0];
     /*结构ip_conntrack_expect位于ip_conntrack.h,这个结构用于将一个预期的连接分配给现有的连接,也就是说本连接是这个master的一个预期连接*/
        /* If we were expected by an expectation, this will be it */
        struct nf_conn *master;//如果该连接是期望连接,指向跟其关联的主连接
    
    #if defined(CONFIG_NF_CONNTRACK_MARK)
        u_int32_t mark;
    #endif
    
    #ifdef CONFIG_NF_CONNTRACK_SECMARK
        u_int32_t secmark;
    #endif
    
        /* Extensions */ /*指向扩展结构,该结构中包含一些基于连接的功能扩展处理函数 */
        struct nf_ct_ext *ext;
    
        /* Storage reserved for other modules, must be the last member */
        union nf_conntrack_proto proto; /*存储特定协议的连接跟踪信息 也就是不同协议实现连接跟踪的额外参数 */
    };
    //其中最主要的就是tuplehash(跟踪连接双方向数据)和status(记录连接状态)
    在status中可以设置的标志,由下面的enum ip_conntrack_status描述,它们可以共存;
    /* Connection state tracking for netfilter.  This is separated from,
       but required by, the NAT layer; it can also be used by an iptables
       extension. */
    enum ip_conntrack_info {
        /* Part of an established connection (either direction).   表示这个数据包对应的连接在两个方向都有数据包通过,
        并且这是ORIGINAL初始方向数据包(无论是TCP、UDP、ICMP数据包,
         只要在该连接的两个方向上已有数据包通过,就会将该连接设置为IP_CT_ESTABLISHED状态。不会根据协议中的标志位进行判断,
         例如TCP的SYN等)。但它表示不了这是第几个数据包,也说明不了这个CT是否是子连接。*/
        IP_CT_ESTABLISHED,
    
        /* Like NEW, but related to an existing connection, or ICMP error
           (in either direction). 表示这个数据包对应的连接还没有REPLY方向数据包,当前数据包是ORIGINAL方向数据包。
           并且这个连接关联一个已有的连接,是该已有连接的子连接,?
           磗tatus标志中已经设置了IPS_EXPECTED标志,该标志在init_conntrack()函数中设置)。但无法
    判断是第几个数据包(不一定是第一个)*/
        IP_CT_RELATED,
    
        /* Started a new connection to track (only
               IP_CT_DIR_ORIGINAL); may be a retransmission. 
               表示这个数据包对应的连接还没有REPLY方向数据包,当前数据包是ORIGINAL方向数据包,该连接不是子连接。但无法判断是
    第几个数据包(不一定是第一个*/
        IP_CT_NEW,
    
        /* >= this indicates reply direction这个状态一般不单独使用,通常以下面两种方式使用 */
        IP_CT_IS_REPLY,
    /* 表示这个数据包对应的连接在两个方向都有数据包通过,并且这是REPLY应答方向数据包。但它表示不了这是
        第几个数据包,也说明不了这个CT是否是子连接。*/
        IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
        /*这个状态仅在nf_conntrack_attach()函数中设置,用于本机返回REJECT,例如返回一个ICMP目的不可达报文,
          或返回一个reset报文。它表示不了这是第几个数据包
        */
        IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
        /* No NEW in reply direction. */
    
        /* Number of distinct IP_CT types. */
        IP_CT_NUMBER,
    
        /* only for userspace compatibility */
    #ifndef __KERNEL__
        IP_CT_NEW_REPLY = IP_CT_NUMBER,
    #endif
    };
    
    #define NF_CT_STATE_INVALID_BIT            (1 << 0)
    #define NF_CT_STATE_BIT(ctinfo)            (1 << ((ctinfo) % IP_CT_IS_REPLY + 1))
    #define NF_CT_STATE_UNTRACKED_BIT        (1 << (IP_CT_NUMBER + 1))
    
    /* Bitset representing status of connection. */
    enum ip_conntrack_status {
        /* It's an expected connection: bit 0 set.  This bit never changed */
        IPS_EXPECTED_BIT = 0,//表示该连接是个子连接 
        IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
    
        /* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
        IPS_SEEN_REPLY_BIT = 1,//表示该连接上双方向上都有数据包了 
        IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
    
        /* Conntrack should never be early-expired. 
        TCP:在三次握手建立完连接后即设定该标志。UDP:如果在该连接上的两个方向都有数据包通过,则再有数据包在该连接上通过时?
        就设定该标志。ICMP:不设置该标志
        */
        IPS_ASSURED_BIT = 2,
    
        IPS_ASSURED = (1 << IPS_ASSURED_BIT),
    
        /* Connection is confirmed: originating packet has left box 
        表示该连接已被添加到net->ct.hash表*/
        IPS_CONFIRMED_BIT = 3,
        IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
    
        /* Connection needs src nat in orig dir.  This bit never changed. 
        在POSTROUTING处,当替换reply tuple完成时, 设置该标记*/
        IPS_SRC_NAT_BIT = 4,
        IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT),
    
        /* Connection needs dst nat in orig dir.  This bit never changed. 
        在PREROUTING处,当替换reply tuple完成时, 设置该标记*/
        IPS_DST_NAT_BIT = 5,
        IPS_DST_NAT = (1 << IPS_DST_NAT_BIT),
    
        /* Both together. */
        IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT),
    
        /* Connection needs TCP sequence adjusted. */
        IPS_SEQ_ADJUST_BIT = 6,
        IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT),
    
        /* NAT initialization bits.  在POSTROUTING处,已被SNAT处理,并被加入到bysource链中,设置该标记*/
        IPS_SRC_NAT_DONE_BIT = 7,
        IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT),
    
        IPS_DST_NAT_DONE_BIT = 8,//在PREROUTING处,已被DNAT处理,并被加入到bysource链中,设置该标记
        IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT),
    
        /* Both together */
        IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE),
    
        /* Connection is dying (removed from lists), can not be unset. 
        表示该连接正在被释放,内核通过该标志保证正在被释放的ct不会被其它地方再次引用。有了这个标志,当某个连接要被删
          除时,即使它还在net->ct.hash中,也不会再次被引 用*/
        IPS_DYING_BIT = 9,
        IPS_DYING = (1 << IPS_DYING_BIT),
    
        /* Connection has fixed timeout. 
         固定连接超时时间,这将不根据状态修改连接超时时间。通过函数nf_ct_refresh_acct()修改超时时间时检查该标志*/
        IPS_FIXED_TIMEOUT_BIT = 10,
        IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
    
        /* Conntrack is a template 
         由CT target进行设置(这个target只能用在raw表中,用于为数据包构建指定ct,并打上该标志),用于表明这个ct是由CT target创建*/
        IPS_TEMPLATE_BIT = 11,
        IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT),
    
        /* Conntrack is a fake untracked entry */
        IPS_UNTRACKED_BIT = 12,
        IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT),
    
        /* Conntrack got a helper explicitly attached via CT target. */
        IPS_HELPER_BIT = 13,
        IPS_HELPER = (1 << IPS_HELPER_BIT),
    };




    /* Connections have two entries in the hash table: one for each way */
    struct nf_conntrack_tuple_hash {
        struct hlist_nulls_node hnnode;
        struct nf_conntrack_tuple tuple;
    };
    nf_ct_ext用于实现对连接跟踪的扩展;
    /* Extensions: optional stuff which isn't permanently in struct. */
    struct nf_ct_ext {
        struct rcu_head rcu;
        u16 offset[NF_CT_EXT_NUM];
        u16 len;
        char data[0];
    };

    nf_conntrack_tuple 的分析:

    /* The protocol-specific manipulable parts of the tuple: always in
     * network order
     */
    union nf_conntrack_man_proto {
        /* Add other protocols here. */
        __be16 all;
    
        struct {
            __be16 port;
        } tcp;
        struct {
            __be16 port;
        } udp;
        struct {
            __be16 id;
        } icmp;
        struct {
            __be16 port;
        } dccp;
        struct {
            __be16 port;
        } sctp;
        struct {
            __be16 key;    /* GRE key is 32bit, PPtP only uses 16bit */
        } gre;
    };
    
    
    /* The manipulable part of the tuple. */
    struct nf_conntrack_man {
        union nf_inet_addr u3; /* 三层识别信息 */
        union nf_conntrack_man_proto u;/* 四层识别信息 */
        /* Layer 3 protocol 三层协议号 */
        u_int16_t l3num;
    };
    
    /* This contains the information to distinguish a connection.
    该结构包含源目的信息用来区分一条连接 */
    struct nf_conntrack_tuple {
        struct nf_conntrack_man src;/* 源, */
        /* These are the parts of the tuple which are fixed. */
        struct { /* 目的,不可操作? */
            union nf_inet_addr u3;
            union {
                /* Add other protocols here. */
                __be16 all;
    
                struct {
                    __be16 port;
                } tcp;
                struct {
                    __be16 port;
                } udp;
                struct {
                    u_int8_t type, code;
                } icmp;
                struct {
                    __be16 port;
                } dccp;
                struct {
                    __be16 port;
                } sctp;
                struct {
                    __be16 key;
                } gre;
            } u;
            /* The protocol.  协议*/
            u_int8_t protonum;
            /* The direction (for tuplehash) */ /* 方向(tuplehash使用) */
            u_int8_t dir;
        } dst;
    };

    Netfilter将每一个数据包转换成tuple,再根据tuple计算出hash值,这样,就可以使用nf_conntrack_hash[hash_id]找到hash表中链表的入口,并组织链表

    http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
  • 相关阅读:
    为App签名(为apk签名)
    Android如何获取网络连接状态(3G/Wifi)及怎样调用网络配置界面
    android textview改变部分文字的颜色和string.xml中文字的替换及部分内容设置颜色、字体、超链接、图片
    Toast和Looper。Handler消息循环机制
    android 创建DateTime类型的数据库
    Android中dp和px之间进行转换
    Java的ThreadPoolExecutor使用几点建议
    ListView中设置item点击状态的背景色
    android 让一个控件按钮居于底部的几种方法
    sqlite3数据类型和函数
  • 原文地址:https://www.cnblogs.com/codestack/p/10850669.html
Copyright © 2020-2023  润新知