netfilter连接跟踪(conntrack)详述
1 连接跟踪来龙去脉
1.1 什么是连接跟踪
连接跟踪顾名思义是对网络连接跟踪,如果将网络比作 高速路 ,连接跟踪就像是高速路里的路上的检查站、摄像头一起的组合,可以记录下你在什么地方进的高速、什么地方下的高速、是谁开了一个什么车等信息。类似连接跟踪会记录下网络中的连接信息,如源ip、目的ip、目前报文的状态、是不是和其他网络连接有关联关系等信息,所以连接跟踪是对报文进行检查并做 记录标记的一个工具。
你可能听说过会话机制,连接跟踪其实就是实现了一种会话机制,当然这里的会话和连接和tcp的会话、连接是有区别的,tcp的会话和连接是协议的一部分,这里的会话和连接是一种标记方法。
1.2 为什么需要连接跟踪
没有连接跟踪有很多问题是不好解决的:
- conntrack是属于netfilter的一部分,netfilter主要功能就是对数据包的过滤及更改,没有连接跟踪只能对单个数据包进行过滤,比如之前的 无状态防火墙 ,有了连接跟踪后,则可以把报文的生命周期延长了,从第一个包到最后一个包都可以关联到一起,就从对 “点” 的防护做到了对 “线” 的防护,就是后来的状态防火墙。
- 有些协议如ftp、sip、tftp等有控制连接和数据连接,两个连接是有从属关系的,如果需要对此类流量进行过滤,没有连接跟踪是不好搞的。
- 最主要的就是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)详述相关推荐
- Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架 — conntrack(CT,连接跟踪)
目录 文章目录 目录 CT CT CT(conntrack,connection tracking,连接跟踪),顾名思义,就是跟踪(并记录)连接的状态,是许多网络应用的基础.例如:iptables.L ...
- linux conntrack命令 路由连接 跟踪表 显示删除监听记录
conntrack命令可以显示,删除和更新跟踪表现有状态条目,还可以监听流事件 1.安装: yum install -y conntrack 2.使用: 查看conntrack表记录 conntrac ...
- Linux内核分析 - 网络[十七]:NetFilter之连接跟踪
内核版本:2.6.34 转载请注明 博客:http://blog.csdn.net/qy532846454 by yoyo 前面章节介绍过Netfilter的框架,地址见:http://blog.cs ...
- 一文读懂 Linux 下单机实现百万并发的内核黑科技:连接跟踪(Conntrack)
公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! 本文介绍连接跟踪(connection tracking,conntrack,CT)的原理,应用,及其在 Linu ...
- Linux网络之连接跟踪(conntrack)
连接跟踪是很多网络服务和应用的基础.例如,kubernetes的service,ServiceMesh sidecar,4层负载均衡软件LVS/IPVS,容器网络,OpenvSwitch,OpenSt ...
- 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协议注 ...
- 连接跟踪(connection tracking,conntrack,CT)
连接跟踪(connection tracking,conntrack,CT) 原文连接:http://arthurchiao.art/blog/conntrack-design-and-impleme ...
- linux视频教程 iptables 跟踪,linux – 了解iptables中的连接跟踪
我在对iptables中的状态/连接跟踪做了一些澄清. >这两条规则有什么区别? iptables -A FORWARD -m state –state ESTABLISHED,RELATED ...
- linux 连接跟踪nf_conntrack 与 NAT和状态防火墙
本文主要记录对于连接跟踪以及其主要应用的NAT和状态iptables的学习内容 连接跟踪 什么是连接跟踪? 连接跟踪是Linux内核中引入的nf_conntrack 模块所实现的功能,同时支持IPv4 ...
最新文章
- Datawhale来厦大啦!
- 将dataGridView数据转成DataTable
- php跳过一段html,PHP_一段能瞬间秒杀所有版本IE的简单HTML代码,许多人都非常讨厌Internet Explore - phpStudy...
- HDU-1233-还是畅通工程(最小生成树)
- 瑞星布局:安全软件的时尚模式
- 解除天翼young和沃派客户端数量限制的方法
- MFC网络通信程序设计——网络聊天室
- 用Flash CS4打开fla文件提示“意外的格式”的解决方法
- Setup Factory操作注册表
- 中卫市地图arcgis数据shp道路地名县区边界水系2021年(下载说明)
- 所有网站都是用虚拟服务器吗,哪类网站不适合使用虚拟主机?
- 三维软件中制作动画导入Unity中使用
- 光圈,焦距,工作距离与景深之间的关系。
- luogu P3527 [POI2011]MET-Meteors
- 量子计算机的运用原理,量子计算机的工作原理和运用?
- 【多项式最小二乘拟合实验】
- uoj 36 玛里苟斯
- Spring context:annotation-config/ 解说
- 201掘安杯网络安全赛web的write up
- ElasticSearch中文拼音后无法高亮
热门文章
- Microsoft Edge浏览器文件保存位置记录
- 交换机与路由器技术-08-路由器上配置DHCP
- VMware ESXi 扩容后提示“无法打开虚拟机的电源,请确认该虚拟磁盘是适用“厚”选项创建的”等信息,执行VMDK 格式是 zeroedthick 还是 eagerzeroedthick
- javax.el.PropertyNotFoundException:类型[com.ex.spring.entity.Dept]上找不到属性[dNo]
- 打印机用计算机名慢,“电脑连上打印机就超级慢”的解决方案
- KubeEdge SIG AI发布首个分布式协同AI Benchmark调研
- 三行情书c语言,“三行情书”——给你三行代码的爱恋~
- lopa分析_AQ/T 3054-2015保护层分析(LOPA)方法应用导则
- Centos 7 开机一直转圈 提示failed to load SELinux policy freezing的解决方法
- 解决github频繁输入密码问题和git@github.com:Permission denied (publickey)问题