1 连接跟踪来龙去脉

1.1 什么是连接跟踪

连接跟踪顾名思义是对网络连接跟踪,如果将网络比作 高速路 ,连接跟踪就像是高速路里的路上的检查站、摄像头一起的组合,可以记录下你在什么地方进的高速、什么地方下的高速、是谁开了一个什么车等信息。类似连接跟踪会记录下网络中的连接信息,如源ip、目的ip、目前报文的状态、是不是和其他网络连接有关联关系等信息,所以连接跟踪是对报文进行检查并做 记录标记的一个工具

你可能听说过会话机制,连接跟踪其实就是实现了一种会话机制,当然这里的会话和连接和tcp的会话、连接是有区别的,tcp的会话和连接是协议的一部分,这里的会话和连接是一种标记方法。

1.2 为什么需要连接跟踪

没有连接跟踪有很多问题是不好解决的:

  1. conntrack是属于netfilter的一部分,netfilter主要功能就是对数据包的过滤及更改,没有连接跟踪只能对单个数据包进行过滤,比如之前的 无状态防火墙 ,有了连接跟踪后,则可以把报文的生命周期延长了,从第一个包到最后一个包都可以关联到一起,就从对 “点” 的防护做到了对 “线” 的防护,就是后来的状态防火墙。
  2. 有些协议如ftp、sip、tftp等有控制连接和数据连接,两个连接是有从属关系的,如果需要对此类流量进行过滤,没有连接跟踪是不好搞的。
  3. 最主要的就是NAT方案,可以解决ipv4地址不够,内网外网连接问题,如果要实现NAT,就需要让内核跟踪会话,设置了CONFIG_NF_CONNTRACK_IPV4就可以构建ipv4 NAT,设置了CONFIG_NF_CONNTRACK_IPV6就可以构建ipv6 NAT.

1.3 连接跟踪的应用场景

应用场景:NAT、状态防火墙、四层负载均衡

状态防火墙
无状态、有状态:最简单的包过滤防火墙是无状态的,而更复杂的防火墙是有状态的
无状态的包过滤防火墙:单独处理每一个数据报
有状态的防火墙能够:通过关联已经或者即将到达的数据包来推断流或者数据报的信息,即那些属于同一个传输关联 (transport association)的数据包或构成同一个IP数据报的IP分片

四层负载均衡:可以根据四层信息(例如 src/dst ip, src/dst port, proto)做流量分发,DNAT实现。

通过负载均衡c1、c2由s1响应服务,c3由s2响应服务。

2 内核中的连接跟踪

2.1 netfilter连接跟踪框架

内核中连接跟踪是在netfilter框架中建立的,netfilter的框架图(来源:wikipedia)如下:

这张图乍一看比较乱,但是这张图非常好的说明了数据流在内核中的过程。
在图中的灰色框标记了conntrack的位置,这个是连接跟踪的起始点,其实在netfilter的所有HOOK点都可以更改**流(flow)**跟踪的信息。

有一个小问题,iptables规则会影响tcpdump抓包吗?
答案是不会,tcpdump抓包是libpcap实现的,libpcap是用bpf(Berkeley Packet Filter)实现的,bpf位于netfilter前,所以不会影响抓包。

如上图所示,更加清楚的标记了连接跟踪的起始位置,Netfilter 会在四个 Hook 点对包进行跟踪:

PRE_ROUTING 和 LOCAL_OUT:调用 nf_conntrack_in() 开始连接跟踪, 正常情况下会创建一条新连接记录,然后将 conntrack entry 放到 unconfirmed list。
为什么是这两个 hook 点呢?因为它们都是新连接的第一个包最先达到的地方,
PRE_ROUTING 是外部主动和本机建连时包最先到达的地方
LOCAL_OUT 是本机主动和外部建连时包最先到达的地方

POST_ROUTING 和 LOCAL_IN:调用 nf_conntrack_confirm() 将 nf_conntrack_in() 创建的连接移到 confirmed list。

同样要问,为什么在这两个 hook 点呢?因为如果新连接的第一个包没有被丢弃,那这 是它们离开 netfilter 之前的最后 hook 点:

外部主动和本机建连的包,如果在中间处理中没有被丢弃,LOCAL_IN 是其被送到应用之前最后的 hook 点
本机主动和外部建连的包,如果在中间处理中没有被丢弃,POST_ROUTING 是其离开主机时的最后 hook 点

确认机制的原因
确认机制可以确保创建了跟踪表的报文真实被接受或转发了,而没有在netfilter中被丢弃。若没有确认机制,包在被netfilter丢弃的情况下,会存在半跟踪状态的数据,浪费了内存和性能。

2.2 重要函数和结构体

重要结构体
struct nf_hook_ops {}: 在HOOK点上注册的连接跟踪信息,通过nf_register_hooks()注册
struct nf_conntrack_tuple {}: 连接跟踪的基本元素,表示特定方向的流。
struct nf_conn {}:连接跟踪条目,定义一个 flow。
在nf_conn中有重要成员:如ct_general、status、master、tuplehash、timeout等。

struct nf_conn {struct nf_conntrack ct_general;spinlock_t    lock;u16        cpu;/* XXX should I move this to the tail ? - Y.K *//* These are my tuples; original and reply */struct nf_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX];/* 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];/* 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_SECMARKu_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;
};

重要函数
hash_conntrack_raw():根据 tuple 计算出一个 32 位的哈希值(hash key)。
nf_conntrack_in():连接跟踪模块的核心,包进入连接跟踪的地方。在此函数中包含下边的步骤:
resolve_normal_ct() -> nf_ct_timeout_lookup()
在resolve_normal_ct() 中会计算元组的散列值,进行匹配,没有就创建nf_conntrack_tuple_hash,将其加入未确认tuplehash列表中,已经创建则判断状态是否超时。

nf_conntrack_confirm():确认前面通过 nf_conntrack_in() 创建的新连接(是否被丢弃),将元组从未确认tuplehash列表中删除。
nf_ct_get(skb, ctinfo):获取连接跟踪数据,没有建立返回null

具体细节可以在内核代码中查看
代码路径:/net/netfilter 一个在线linux内核代码的网站

2.3 连接跟踪流程概览


可以在流程中看到ipv4_helper(),在这里可以调用辅助连接跟踪,实现对ftp、sip等协议的跟踪,具体见下文。

2.4 连接跟踪ftp实例

ftp连接跟踪的实现依赖于netfilter中的辅助方法期望连接
辅助方法
对于ftp、sip这种数据流和控制流不同的协议,netfilter需要知道哪些流是彼此相关的,就是确定主连接和从属连接,于是在之前的基础上加入了辅助方法,可以找到关联的流。在具体实现中需要对主连接的包进行做关键字匹配,从而找到从属连接信息。

期望连接
就是在匹配关键字确定了从属连接信息后,提前建立的连接跟踪信息(就是连接未到跟踪先行,避免从属连接被当成普通连接处理)
内核知道两条连接的关系后,netfilter就可以在主连接上生成适用于相关连接的规则,如:

#接受跟踪状态为RELATED的数据包
iptables -A INPUT -m conntrack --ctstate RELATED -j ACCEPT

FTP的辅助连接跟踪是在net/netfilter/nf_conntrack_ftp.c实现的,核心函数是help() ,它最终作为struct nf_conntrack_helper ftp的一部分,通过nf_conntrack_ftp_init(void)nf_conntrack_helper_register() 注册到了连接跟踪方法散列表nf_ct_helper_hash 中,被上文提到的 ipv4_helper() 调用。

static int help(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct,enum ip_conntrack_info ctinfo)
{...//上面介绍的序号缓存信息struct nf_ct_ftp_master *ct_ftp_info = &nfct_help(ct)->help.ct_ftp_info;struct nf_conntrack_expect *exp;...//略去部分代码...//从连接跟踪信息块中找到skb传输方向上的地址信息cmd.l3num = ct->tuplehash[dir].tuple.src.l3num;memcpy(cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all, sizeof(cmd.u3.all));//搜索数据包的内容,从中寻找是否有特定的关键字(PORT和PASV信息)/********************关键部分****************/for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {found = find_pattern(fb_ptr, datalen, search[dir][i].pattern,search[dir][i].plen, search[dir][i].skip, search[dir][i].term,&matchoff, &matchlen, &cmd, search[dir][i].getnum);if (found)break;}//虽然连接跟踪本意上不应该丢包,但是遇到这种部分匹配的异常情况,//说明传输确实是有问题的,必须丢包才能更准确的跟踪if (found == -1) {ret = NF_DROP;goto out;} else if (found == 0) {//没有包含关注的关键字,尝试更新序列号缓存信息后结束     ret = NF_ACCEPT;goto out_update_nl;}//找到了我们关心的信息,说明即将要建立一个数据连接(即期望连接),//所以分配一个期望并对其进行初始化,然后将其加入期望连接表中,这样//等期望连接的数据包到达时就可以匹配到了exp = nf_ct_expect_alloc(ct);if (exp == NULL) {ret = NF_DROP;goto out;}//计算期望连接的tuple信息.../*略去部分代码*/...//初始化期望连接结构nf_ct_expect_init(exp, cmd.l3num, &ct->tuplehash[!dir].tuple.src.u3, daddr,IPPROTO_TCP, NULL, &cmd.u.tcp.port);/* Now, NAT might want to mangle the packet, and register the* (possibly changed) expectation itself. *///如果NAT会处理该数据包,则交给NAT处理,否则将其加入到期望连接跟踪表中nf_nat_ftp = rcu_dereference(nf_nat_ftp_hook);if (nf_nat_ftp && ct->status & IPS_NAT_MASK)ret = nf_nat_ftp(skb, ctinfo, search[dir][i].ftptype, matchoff, matchlen, exp);else {/* Can't expect this?  Best to drop packet now. */if (nf_ct_expect_related(exp) != 0)ret = NF_DROP;elseret = NF_ACCEPT;}
out_put_expect:nf_ct_expect_put(exp);
out_update_nl://只有当前数据包是以'\n'结尾时才更新序号缓存数组if (ends_in_nl)update_nl_seq(seq, ct_ftp_info, dir, skb);out:spin_unlock_bh(&nf_ftp_lock);return ret;
}

还有一个关键的结构体,用来做关键字匹配

static struct ftp_search {const char *pattern;size_t plen;char skip;char term;enum nf_ct_ftp_type ftptype;int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char, unsigned int *);
} search[IP_CT_DIR_MAX][2] = {[IP_CT_DIR_ORIGINAL] = {{.pattern   = "PORT",.plen       = sizeof("PORT") - 1,.skip       = ' ',.term      = '\r',.ftptype  = NF_CT_FTP_PORT,.getnum       = try_rfc959,},{.pattern   = "EPRT",.plen       = sizeof("EPRT") - 1,.skip       = ' ',.term      = '\r',.ftptype  = NF_CT_FTP_EPRT,.getnum       = try_eprt,},},[IP_CT_DIR_REPLY] = {{.pattern = "227 ",.plen       = sizeof("227 ") - 1,.ftptype    = NF_CT_FTP_PASV,.getnum       = try_rfc1123,},{.pattern  = "229 ",.plen       = sizeof("229 ") - 1,.skip       = '(',.term      = ')',.ftptype   = NF_CT_FTP_EPSV,.getnum       = try_epsv_response,},},
};

现有一ftp报文如下:

具体匹配过程是,ftp报文进入netfilter,通过HOOK点的help函数处理时,id为35的报文通过 .pattern = 227 成功匹配,应为此报文的reponse code 为227,然后调用解析函数try_rfc1123() 具体如下:

static int try_rfc1123(const char *data, size_t dlen,struct nf_conntrack_man *cmd, char term,unsigned int *offset)
{int i;for (i = 0; i < dlen; i++)if (isdigit(data[i]))break;if (i == dlen)return 0;*offset += i;return try_rfc959(data + i, dlen - i, cmd, 0, offset);
}

然后调用try_rfc959() 解析,函数如下:

static int try_rfc959(const char *data, size_t dlen,struct nf_conntrack_man *cmd, char term,unsigned int *offset)
{int length;u_int32_t array[6];length = try_number(data, dlen, array, 6, ',', term);if (length == 0)return 0;cmd->u3.ip =  htonl((array[0] << 24) | (array[1] << 16) |(array[2] << 8) | array[3]);cmd->u.tcp.port = htons((array[4] << 8) | array[5]);return length;
}

try_number() 函数是将字符转为数字,因为ftp的data传递的均为字符,所以查找端口需要转换为int
报文的端口信息是 100,85 ,100<<8 | 85 = 25685 即为数据传输协程的端口号。由ip和端口可以创建期望连接 ,当数据流到来时,在调用方法init_conntrack() 创建连接时,会检查是否有期望,发现期望的话,就将IPS_EXPECTED_BIT置位。

还有一个问题,就是内核如何知道哪些连接需要使用ftp辅助方法?
辅助方法会侦听预先定义ftp的端口,发现此端口的连接,则会调用辅助方法。ftp端口可以设置:

modprobe nf_conntrack_ftp ports=2121,2120,2310
或者使用CT目标
iptables -A PREROUTING -t raw -p tcp --dport 2121 -j CT --helper ftp

3 扩展连接跟踪

在某些情况下,比如为了增加性能,做自定义功能时,需要使用拓展连接跟踪,有些拓展需要由sysctrls开启,或者是用iptables规则触发,可以根据mark标志位写iptables规则来触发拓展连接跟踪的处理。步骤如下:
首先要定义自己的拓展id
位于net/netfilter/nf_conntrack_extend.h中

enum nf_ct_ext_id {NF_CT_EXT_HELPER,NF_CT_EXT_NAT,NF_CT_EXT_SEQADJ,NF_CT_EXT_ACCT,NF_CT_EXT_ECACHE,NF_CT_EXT_ZONE,NF_CT_EXT_TSTAMP,NF_CT_EXT_TIMEOUT,NF_CT_EXT_LABELS,NF_CT_EXT_SYNPROXY,/*在此添加自己的拓展id如下*/NF_CT_EXT_FASTPASS,NF_CT_EXT_NUM,
};

然后需要定义自己的nf_ct_ext_type对象

static struct nf_ct_ext_type dpi_extend __read_mostly = {.len    = sizeof(struct fastpass_extend_info),.align  = __alignof__(struct fastpass_extend_info),.id     = NF_CT_EXT_FASTPASS,  .destroy = NULL,.flags   = NF_CT_EXT_F_PREALLOC,
};

其中,fastpass_extend_info结构如下:

struct dpi_extend_info{unsigned int fast_status;unsigned int (*fastpass_process)(struct sk_buff *skb, const struct xt_action_param *par);
};

此结构可以调用方法nf_ct_ext_add(ct,NF_CT_EXT_FASTPASS, GFP_ATOMIC) 写到nf_conn的struct nf_ct_ext *ext中,可以通过**nf_ct_ext_find()**查找是否有nf_ct_ext的存在。

最后通过
nf_ct_extend_register(&dpi_extend); 注册拓展连接跟踪
nf_ct_extend_unregister(&dpi_extend); 注销拓展连接跟踪

具体可以参考/net/netfilter下的nf_conntrack_timestamp.c等。

4 工具

常用命令

查看nf_conntrack表当前连接数
cat /proc/sys/net/netfilter/nf_conntrack_count       查看nf_conntrack表最大连接数
cat /proc/sys/net/netfilter/nf_conntrack_max    通过dmesg可以查看nf_conntrack的状况:
dmesg |grep nf_conntrack查看存储conntrack条目的哈希表大小,此为只读文件
cat /proc/sys/net/netfilter/nf_conntrack_buckets查看nf_conntrack的TCP连接记录时间
cat /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established通过内核参数查看命令,查看所有参数配置
sysctl -a | grep nf_conntrack通过conntrack命令行工具查看conntrack的内容
yum install -y conntrack
conntrack -L

nf_conntrack相关内核参数和解释

5 总结

连接跟踪的作用有很多,当然有时也会有副作用,比如会降低系统的性能,当对性能要求较高,且确定不需要此功能时可以将其关掉。

$ sysctl -a | grep nf_conntrack
net.netfilter.nf_conntrack_acct = 0
net.netfilter.nf_conntrack_buckets = 262144                 # hashsize = nf_conntrack_max/nf_conntrack_buckets
net.netfilter.nf_conntrack_checksum = 1
net.netfilter.nf_conntrack_count = 2148
... # DCCP options
net.netfilter.nf_conntrack_events = 1
net.netfilter.nf_conntrack_expect_max = 1024
... # IPv6 options
net.netfilter.nf_conntrack_generic_timeout = 600
net.netfilter.nf_conntrack_helper = 0
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_log_invalid = 0
net.netfilter.nf_conntrack_max = 1048576                    # conntrack table size
... # SCTP options
net.netfilter.nf_conntrack_tcp_be_liberal = 0
net.netfilter.nf_conntrack_tcp_loose = 1
net.netfilter.nf_conntrack_tcp_max_retrans = 3
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 21600
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 120
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_timestamp = 0
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180

参考:
http://arthurchiao.art/blog/conntrack-design-and-implementation-zh/
https://blog.csdn.net/whatday/article/details/105251137
https://blog.csdn.net/qq_41453285/article/details/98944149
https://blog.csdn.net/xiaoyu_750516366/article/details/89076015
精通linux内核网络
https://my.oschina.net/u/4269090/blog/3328896

netfilter连接跟踪(conntrack)详述相关推荐

  1. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架 — conntrack(CT,连接跟踪)

    目录 文章目录 目录 CT CT CT(conntrack,connection tracking,连接跟踪),顾名思义,就是跟踪(并记录)连接的状态,是许多网络应用的基础.例如:iptables.L ...

  2. linux conntrack命令 路由连接 跟踪表 显示删除监听记录

    conntrack命令可以显示,删除和更新跟踪表现有状态条目,还可以监听流事件 1.安装: yum install -y conntrack 2.使用: 查看conntrack表记录 conntrac ...

  3. Linux内核分析 - 网络[十七]:NetFilter之连接跟踪

    内核版本:2.6.34 转载请注明 博客:http://blog.csdn.net/qy532846454 by yoyo 前面章节介绍过Netfilter的框架,地址见:http://blog.cs ...

  4. 一文读懂 Linux 下单机实现百万并发的内核黑科技:连接跟踪(Conntrack)

    公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! 本文介绍连接跟踪(connection tracking,conntrack,CT)的原理,应用,及其在 Linu ...

  5. Linux网络之连接跟踪(conntrack)

    连接跟踪是很多网络服务和应用的基础.例如,kubernetes的service,ServiceMesh sidecar,4层负载均衡软件LVS/IPVS,容器网络,OpenvSwitch,OpenSt ...

  6. linux内核协议栈 netfilter 之连接跟踪子系统的L3 L4协议栈模块初始化与自定义注册

    目录 1 L3.L4协议跟踪初始化 nf_conntrack_proto_init() 1.1 L3协议管理 1.1.1 struct nf_conntrack_l3proto 1.1.2 L3协议注 ...

  7. 连接跟踪(connection tracking,conntrack,CT)

    连接跟踪(connection tracking,conntrack,CT) 原文连接:http://arthurchiao.art/blog/conntrack-design-and-impleme ...

  8. linux视频教程 iptables 跟踪,linux – 了解iptables中的连接跟踪

    我在对iptables中的状态/连接跟踪做了一些澄清. >这两条规则有什么区别? iptables -A FORWARD -m state –state ESTABLISHED,RELATED ...

  9. linux 连接跟踪nf_conntrack 与 NAT和状态防火墙

    本文主要记录对于连接跟踪以及其主要应用的NAT和状态iptables的学习内容 连接跟踪 什么是连接跟踪? 连接跟踪是Linux内核中引入的nf_conntrack 模块所实现的功能,同时支持IPv4 ...

最新文章

  1. Datawhale来厦大啦!
  2. 将dataGridView数据转成DataTable
  3. php跳过一段html,PHP_一段能瞬间秒杀所有版本IE的简单HTML代码,许多人都非常讨厌Internet Explore - phpStudy...
  4. HDU-1233-还是畅通工程(最小生成树)
  5. 瑞星布局:安全软件的时尚模式
  6. 解除天翼young和沃派客户端数量限制的方法
  7. MFC网络通信程序设计——网络聊天室
  8. 用Flash CS4打开fla文件提示“意外的格式”的解决方法
  9. Setup Factory操作注册表
  10. 中卫市地图arcgis数据shp道路地名县区边界水系2021年(下载说明)
  11. 所有网站都是用虚拟服务器吗,哪类网站不适合使用虚拟主机?
  12. 三维软件中制作动画导入Unity中使用
  13. 光圈,焦距,工作距离与景深之间的关系。
  14. luogu P3527 [POI2011]MET-Meteors
  15. 量子计算机的运用原理,量子计算机的工作原理和运用?
  16. 【多项式最小二乘拟合实验】
  17. uoj 36 玛里苟斯
  18. Spring context:annotation-config/ 解说
  19. 201掘安杯网络安全赛web的write up
  20. ElasticSearch中文拼音后无法高亮

热门文章

  1. Microsoft Edge浏览器文件保存位置记录
  2. 交换机与路由器技术-08-路由器上配置DHCP
  3. VMware ESXi 扩容后提示“无法打开虚拟机的电源,请确认该虚拟磁盘是适用“厚”选项创建的”等信息,执行VMDK 格式是 zeroedthick 还是 eagerzeroedthick
  4. javax.el.PropertyNotFoundException:类型[com.ex.spring.entity.Dept]上找不到属性[dNo]
  5. 打印机用计算机名慢,“电脑连上打印机就超级慢”的解决方案
  6. KubeEdge SIG AI发布首个分布式协同AI Benchmark调研
  7. 三行情书c语言,“三行情书”——给你三行代码的爱恋~
  8. lopa分析_AQ/T 3054-2015保护层分析(LOPA)方法应用导则
  9. Centos 7 开机一直转圈 提示failed to load SELinux policy freezing的解决方法
  10. 解决github频繁输入密码问题和git@github.com:Permission denied (publickey)问题