连接跟踪与netfilter挂载点

netfilter防火墙 专栏收录该内容
13 篇文章 1 订阅

内核连接跟踪模块conntrack在netfilter的基础上实现,主要与netfilter的NF_INET_PRE_ROUTING、NF_INET_LOCAL_OUT、NF_INET_LOCAL_IN和NF_INET_POST_ROUTING四个hook点相关。conntrack与NF_INET_FORWARD转发hook点没有交互。

根据数据包的流向,conntrack实现了两类四个函数,分别为进入netfilter系统的处理函数,包括ipv4_conntrack_in和ipv4_conntrack_local函数,离开netfilter系统的处理函数,包括ipv4_helper和ipv4_confirm函数。


如下所示为进入netfilter系统的两个函数,核心处理都是由nf_conntrack_in实现,函数ipv4_conntrack_in处理的是由系统外部进入netfilter的NF_INET_PRE_ROUTING挂载点的数据包;ipv4_conntrack_local函数处理的是由系统应用层进入netfilter的NF_INET_LOCAL_OUT挂载点的数据包。与前者不同,对于IP头部信息不完整或者分片的数据包,ipv4_conntrack_local函数不做进一步的处理。核心函数nf_conntrack_in根据数据包的特征(源IP、目的IP,源端口、目的端口和协议号等)创建连接跟踪信息,由结构体nf_conn表示,并将其添加到当前网络命名空间每处理器的unconfirmed哈希链表中。

static unsigned int ipv4_conntrack_in(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
    return nf_conntrack_in(state->net, PF_INET, state->hook, skb);
}
static unsigned int ipv4_conntrack_local(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
    if (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr))
        return NF_ACCEPT;
    if (ip_is_fragment(ip_hdr(skb)))
        return NF_ACCEPT;

    return nf_conntrack_in(state->net, PF_INET, state->hook, skb);
}

static const struct nf_hook_ops ipv4_conntrack_ops[] = {
    {
        .hook       = ipv4_conntrack_in,
        .pf     = NFPROTO_IPV4,
        .hooknum    = NF_INET_PRE_ROUTING,
        .priority   = NF_IP_PRI_CONNTRACK,
    },
    {
        .hook       = ipv4_conntrack_local,
        .pf     = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_OUT,
        .priority   = NF_IP_PRI_CONNTRACK,
    },
}

在离开netfilter系统的方向上有ipv4_helper和ipv4_confirm两个处理函数,两者都挂载在NF_INET_POST_ROUTING和NF_INET_LOCAL_IN两个hook点上。hook点NF_INET_POST_ROUTING是数据包由netfilter发往外部的数据包,NF_INET_LOCAL_IN是由netfilter发往本机应用层的数据包。ipv4_helper函数的优先级高于ipv4_confirm函数,实际上ipv4_confirm函数使用的是netfilter系统中最低的优先级,最后执行。

static const struct nf_hook_ops ipv4_conntrack_ops[] = {
    {
        .hook       = ipv4_helper,
        .pf     = NFPROTO_IPV4,
        .hooknum    = NF_INET_POST_ROUTING,
        .priority   = NF_IP_PRI_CONNTRACK_HELPER,
    },
    {
        .hook       = ipv4_confirm,
        .pf     = NFPROTO_IPV4,
        .hooknum    = NF_INET_POST_ROUTING,
        .priority   = NF_IP_PRI_CONNTRACK_CONFIRM,
    },
    {
        .hook       = ipv4_helper,
        .pf     = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_IN,
        .priority   = NF_IP_PRI_CONNTRACK_HELPER,
    },
    {
        .hook       = ipv4_confirm,
        .pf     = NFPROTO_IPV4,
        .hooknum    = NF_INET_LOCAL_IN,
        .priority   = NF_IP_PRI_CONNTRACK_CONFIRM,
    },
};

函数ipv4_confirm最终调用__nf_conntrack_confirm进行具体的处理,其将之前创建的连接跟踪结构nf_conn从unconfirmed链表移除,添加到全局的nf_conntrack_hash链表中。参见函数__nf_conntrack_hash_insert,其将数据流两个方向(IP_CT_DIR_ORIGINAL和IP_CT_DIR_REPLY)添加到了由不同hash值(流特征计算而来)索引的全局nf_conntrack_hash链表中。

int __nf_conntrack_confirm(struct sk_buff *skb)
{
    nf_ct_del_from_dying_or_unconfirmed_list(ct);
    ct->status |= IPS_CONFIRMED;
 
    __nf_conntrack_hash_insert(ct, hash, reply_hash);
}
static void __nf_conntrack_hash_insert(struct nf_conn *ct, unsigned int hash, unsigned int reply_hash)
{
    hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode, &nf_conntrack_hash[hash]);
    hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode, &nf_conntrack_hash[reply_hash]);
}

对于配置了NAT转发以及防火墙功能的Linux系统,将导致诸如SIP、H323、ftp等的协议不能正常工作,此类协议,一般有两种流量组成:控制流和数据流,其中控制流用于协商数据流的参数其中包括IP地址与端口信息等。NAT改变了IP地址和端口号导致其数据流受阻。函数ipv4_helper用于处理此种情况。例如SIP协议的helper函数sip_help_udp和sip_help_tcp用于修正SIP协议中的媒体流协商参数。

static unsigned int ipv4_helper(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
    struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    const struct nf_conn_help *help;
    const struct nf_conntrack_helper *helper;

    ct = nf_ct_get(skb, &ctinfo);
    if (!ct || ctinfo == IP_CT_RELATED_REPLY)
        return NF_ACCEPT;

    help = nfct_help(ct);

    helper = rcu_dereference(help->helper);
    return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb), ct, ctinfo);
}

 

内核版本 4.15.0

 

  • 0
    点赞
  • 0
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值