网络地址转换(NAT)之报文跟踪
网络地址转换(NAT)之报文跟踪
来源 https://fedoramagazine.org/network-address-translation-part-1-packet-tracing/
参考 https://linux.cn/article-13364-1.html
这是有关 网络地址转换(network address translation)(NAT)的系列文章中的第一篇。这一部分将展示如何使用 iptables/nftables 报文跟踪功能来定位 NAT 相关的连接问题。
引言
网络地址转换(NAT)是一种将容器或虚拟机暴露在互联网中的一种方式。传入的连接请求将其目标地址改写为另一个地址,随后被路由到容器或虚拟机。相同的技术也可用于负载均衡,即传入的连接被分散到不同的服务器上去。
当网络地址转换没有按预期工作时,连接请求将失败,会暴露错误的服务,连接最终出现在错误的容器中,或者请求超时,等等。调试此类问题的一种方法是检查传入请求是否与预期或已配置的转换相匹配。
连接跟踪
NAT 不仅仅是修改 IP 地址或端口号。例如,在将地址 X 映射到 Y 时,无需添加新规则来执行反向转换。一个被称为 “conntrack” 的 netfilter 系统可以识别已有连接的回复报文。每个连接都在 conntrack 系统中有自己的 NAT 状态。反向转换是自动完成的。
规则匹配跟踪
nftables 工具(以及在较小的程度上,iptables)允许针对某个报文检查其处理方式以及该报文匹配规则集合中的哪条规则。为了使用这项特殊的功能,可在合适的位置插入“跟踪规则”。这些规则会选择被跟踪的报文。假设一个来自 IP 地址 C 的主机正在访问一个 IP 地址是 S 以及端口是 P 的服务。我们想知道报文匹配了哪条 NAT 转换规则,系统检查了哪些规则,以及报文是否在哪里被丢弃了。
由于我们要处理的是传入连接,所以我们将规则添加到 prerouting 钩子上。prerouting 意味着内核尚未决定将报文发往何处。修改目标地址通常会使报文被系统转发,而不是由主机自身处理。
初始配置
# nft 'add table inet trace_debug'
# nft 'add chain inet trace_debug trace_pre { type filter hook prerouting priority -200000; }'
# nft "insert rule inet trace_debug trace_pre ip saddr $C ip daddr $S tcp dport $P tcp flags syn limit rate 1/second meta nftrace set 1"
第一条规则添加了一张新的规则表,这使得将来删除和调试规则可以更轻松。一句 nft delete table inet trace_debug
命令就可以删除调试期间临时加入表中的所有规则和链。
第二条规则在系统进行路由选择之前(prerouting
钩子)创建了一个基本钩子,并将其优先级设置为负数,以保证它在连接跟踪流程和 NAT 规则匹配之前被执行。
然而,唯一最重要的部分是第三条规则的最后一段:meta nftrace set 1
。这条规则会使系统记录所有匹配这条规则的报文所关联的事件。为了尽可能高效地查看跟踪信息(提高信噪比),考虑对跟踪的事件增加一个速率限制,以保证其数量处于可管理的范围。一个好的选择是限制每秒钟最多一个报文或一分钟最多一个报文。上述案例记录了所有来自终端 $C
且去往终端 $S
的端口 $P
的所有 SYN 报文和 SYN/ACK 报文。限制速率的配置语句可以防范事件过多导致的洪泛风险。事实上,大多数情况下只记录一个报文就足够了。
对于 iptables 用户来讲,配置流程是类似的。等价的配置规则类似于:
# iptables -t raw -I PREROUTING -s $C -d $S -p tcp --tcp-flags SYN SYN --dport $P -m limit --limit 1/s -j TRACE
获取跟踪事件
原生 nft 工具的用户可以直接运行 nft
进入 nft 跟踪模式:
# nft monitor trace
这条命令会将收到的报文以及所有匹配该报文的规则打印出来(用 CTRL-C
来停止输出):
trace id f0f627 ip raw prerouting packet: iif "veth0" ether saddr ..
我们将在下一章详细分析该结果。如果你用的是 iptables,首先通过 iptables –version
命令检查一下已安装的版本。例如:
# iptables --version
iptables v1.8.5 (legacy)
(legacy)
意味着被跟踪的事件会被记录到内核的环形缓冲区中。你可以用 dmesg
或 journalctl
命令来查看这些事件。这些调试输出缺少一些信息,但和新工具提供的输出从概念上来讲很类似。你将需要首先查看规则被记录下来的行号,并与活跃的 iptables 规则集合手动关联。如果输出显示 (nf_tables)
,你可以使用 xtables-monitor
工具:
# xtables-monitor --trace
如果上述命令仅显示版本号,你仍然需要查看 dmesg
/journalctl
的输出。xtables-monitor
工具和 nft
监控跟踪工具使用相同的内核接口。它们之间唯一的不同点就是,xtables-monitor
工具会用 iptables
的语法打印事件,且如果你同时使用了 iptables-nft
和 nft
,它将不能打印那些使用了 maps/sets 或其他只有 nftables 才支持的功能的规则。
示例
我们假设需要调试一个到虚拟机/容器的端口不通的问题。ssh -p 1222 10.1.2.3
命令应该可以远程连接那台服务器上的某个容器,但连接请求超时了。
你拥有运行那台容器的主机的登录权限。现在登录该机器并增加一条跟踪规则。可通过前述案例查看如何增加一个临时的调试规则表。跟踪规则类似于这样:
nft "insert rule inet trace_debug trace_pre ip daddr 10.1.2.3 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1"
在添加完上述规则后,运行 nft monitor trace
,在跟踪模式下启动 nft,然后重试刚才失败的 ssh
命令。如果规则集较大,会出现大量的输出。不用担心这些输出,下一节我们会做逐行分析。
trace id 9c01f8 inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
trace id 9c01f8 inet trace_debug trace_pre rule ip daddr 10.2.1.2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue)
trace id 9c01f8 inet trace_debug trace_pre verdict continue
trace id 9c01f8 inet trace_debug trace_pre policy accept
trace id 9c01f8 inet nat prerouting packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
trace id 9c01f8 inet nat prerouting rule ip daddr 10.1.2.3 tcp dport 1222 dnat ip to 192.168.70.10:22 (verdict accept)
trace id 9c01f8 inet filter forward packet: iif "enp0" oif "veth21" ether saddr .. ip daddr 192.168.70.10 .. tcp dport 22 tcp flags == syn tcp window 29200
trace id 9c01f8 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop)
trace id 20a4ef inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
对跟踪结果作逐行分析
输出结果的第一行是触发后续输出的报文编号。这一行的语法与 nft 规则语法相同,同时还包括了接收报文的首部字段信息。你也可以在这一行找到接收报文的接口名称(此处为 enp0
)、报文的源和目的 MAC 地址、报文的源 IP 地址(可能很重要 - 报告问题的人可能选择了一个错误的或非预期的主机),以及 TCP 的源和目的端口。同时你也可以在这一行的开头看到一个“跟踪编号”。该编号标识了匹配跟踪规则的特定报文。第二行包括了该报文匹配的第一条跟踪规则:
trace id 9c01f8 inet trace_debug trace_pre rule ip daddr 10.2.1.2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue)
这就是刚添加的跟踪规则。这里显示的第一条规则总是激活报文跟踪的规则。如果在这之前还有其他规则,它们将不会在这里显示。如果没有任何跟踪输出结果,说明没有抵达这条跟踪规则,或者没有匹配成功。下面的两行表明没有后续的匹配规则,且 trace_pre
钩子允许报文继续传输(判定为接受)。
下一条匹配规则是:
trace id 9c01f8 inet nat prerouting rule ip daddr 10.1.2.3 tcp dport 1222 dnat ip to 192.168.70.10:22 (verdict accept)
这条 DNAT 规则设置了一个到其他地址和端口的映射。规则中的参数 192.168.70.10
是需要收包的虚拟机的地址,目前为止没有问题。如果它不是正确的虚拟机地址,说明地址输入错误,或者匹配了错误的 NAT 规则。
IP 转发
通过下面的输出我们可以看到,IP 路由引擎告诉 IP 协议栈,该报文需要被转发到另一个主机:
trace id 9c01f8 inet filter forward packet: iif "enp0" oif "veth21" ether saddr .. ip daddr 192.168.70.10 .. tcp dport 22 tcp flags == syn tcp window 29200
这是接收到的报文的另一种呈现形式,但和之前相比有一些有趣的不同。现在的结果有了一个输出接口集合。这在之前不存在的,因为之前的规则是在路由决策之前(prerouting
钩子)。跟踪编号和之前一样,因此仍然是相同的报文,但目标地址和端口已经被修改。假设现在还有匹配 tcp dport 1222
的规则,它们将不会对现阶段的报文产生任何影响了。
如果该行不包含输出接口(oif
),说明路由决策将报文路由到了本机。对路由过程的调试属于另外一个主题,本文不再涉及。
trace id 9c01f8 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
这条输出表明,报文匹配到了一个跳转到 allowed_dnats
链的规则。下一行则说明了连接失败的根本原因:
trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop)
这条规则无条件地将报文丢弃,因此后续没有关于该报文的日志输出。下一行则是另一个报文的输出结果了:
trace id 20a4ef inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
跟踪编号已经和之前不一样,然后报文的内容却和之前是一样的。这是一个重传尝试:第一个报文被丢弃了,因此 TCP 尝试了重传。可以忽略掉剩余的输出结果了,因为它并没有提供新的信息。现在是时候检查那条链了。
规则集合分析
上一节我们发现报文在 inet filter 表中的一个名叫 allowed_dnats
的链中被丢弃。现在我们来查看它:
# nft list chain inet filter allowed_dnats
table inet filter {
chain allowed_dnats {
meta nfproto ipv4 ip daddr . tcp dport @allow_in accept
drop
}
}
接受 @allow_in
集的数据包的规则没有显示在跟踪日志中。我们通过列出元素的方式,再次检查上述报文的目标地址是否在 @allow_in
集中:
# nft "get element inet filter allow_in { 192.168.70.10 . 22 }"
Error: Could not process rule: No such file or directory
不出所料,地址-服务对并没有出现在集合中。我们将其添加到集合中。
# nft "add element inet filter allow_in { 192.168.70.10 . 22 }"
现在运行查询命令,它将返回新添加的元素。
# nft "get element inet filter allow_in { 192.168.70.10 . 22 }"
table inet filter {
set allow_in {
type ipv4_addr . inet_service
elements = { 192.168.70.10 . 22 }
}
}
ssh
命令现在应该可以工作,且跟踪结果可以反映出该变化:
trace id 497abf58 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
trace id 497abf58 inet filter allowed_dnats rule meta nfproto ipv4 ip daddr . tcp dport @allow_in accept (verdict accept)
trace id 497abf58 ip postrouting packet: iif "enp0" oif "veth21" ether .. trace id 497abf58 ip postrouting policy accept
这表明报文通过了转发路径中的最后一个钩子 - postrouting
。
如果现在仍然无法连接,问题可能处在报文流程的后续阶段,有可能并不在 nftables 的规则集合范围之内。
总结
本文介绍了如何通过 nftables 的跟踪机制检查丢包或其他类型的连接问题。本系列的下一篇文章将展示如何检查连接跟踪系统和可能与连接跟踪流相关的 NAT 信息。
============= End
这是有关 网络地址转换(network address translation)(NAT)的系列文章中的第一篇。这一部分将展示如何使用 iptables/nftables 报文跟踪功能来定位 NAT 相关的连接问题。
引言
网络地址转换(NAT)是一种将容器或虚拟机暴露在互联网中的一种方式。传入的连接请求将其目标地址改写为另一个地址,随后被路由到容器或虚拟机。相同的技术也可用于负载均衡,即传入的连接被分散到不同的服务器上去。
当网络地址转换没有按预期工作时,连接请求将失败,会暴露错误的服务,连接最终出现在错误的容器中,或者请求超时,等等。调试此类问题的一种方法是检查传入请求是否与预期或已配置的转换相匹配。
连接跟踪
NAT 不仅仅是修改 IP 地址或端口号。例如,在将地址 X 映射到 Y 时,无需添加新规则来执行反向转换。一个被称为 “conntrack” 的 netfilter 系统可以识别已有连接的回复报文。每个连接都在 conntrack 系统中有自己的 NAT 状态。反向转换是自动完成的。
规则匹配跟踪
nftables 工具(以及在较小的程度上,iptables)允许针对某个报文检查其处理方式以及该报文匹配规则集合中的哪条规则。为了使用这项特殊的功能,可在合适的位置插入“跟踪规则”。这些规则会选择被跟踪的报文。假设一个来自 IP 地址 C 的主机正在访问一个 IP 地址是 S 以及端口是 P 的服务。我们想知道报文匹配了哪条 NAT 转换规则,系统检查了哪些规则,以及报文是否在哪里被丢弃了。
由于我们要处理的是传入连接,所以我们将规则添加到 prerouting 钩子上。prerouting 意味着内核尚未决定将报文发往何处。修改目标地址通常会使报文被系统转发,而不是由主机自身处理。
初始配置
# nft 'add table inet trace_debug'
# nft 'add chain inet trace_debug trace_pre { type filter hook prerouting priority -200000; }'
# nft "insert rule inet trace_debug trace_pre ip saddr $C ip daddr $S tcp dport $P tcp flags syn limit rate 1/second meta nftrace set 1"
第一条规则添加了一张新的规则表,这使得将来删除和调试规则可以更轻松。一句 nft delete table inet trace_debug
命令就可以删除调试期间临时加入表中的所有规则和链。
第二条规则在系统进行路由选择之前(prerouting
钩子)创建了一个基本钩子,并将其优先级设置为负数,以保证它在连接跟踪流程和 NAT 规则匹配之前被执行。
然而,唯一最重要的部分是第三条规则的最后一段:meta nftrace set 1
。这条规则会使系统记录所有匹配这条规则的报文所关联的事件。为了尽可能高效地查看跟踪信息(提高信噪比),考虑对跟踪的事件增加一个速率限制,以保证其数量处于可管理的范围。一个好的选择是限制每秒钟最多一个报文或一分钟最多一个报文。上述案例记录了所有来自终端 $C
且去往终端 $S
的端口 $P
的所有 SYN 报文和 SYN/ACK 报文。限制速率的配置语句可以防范事件过多导致的洪泛风险。事实上,大多数情况下只记录一个报文就足够了。
对于 iptables 用户来讲,配置流程是类似的。等价的配置规则类似于:
# iptables -t raw -I PREROUTING -s $C -d $S -p tcp --tcp-flags SYN SYN --dport $P -m limit --limit 1/s -j TRACE
获取跟踪事件
原生 nft 工具的用户可以直接运行 nft
进入 nft 跟踪模式:
# nft monitor trace
这条命令会将收到的报文以及所有匹配该报文的规则打印出来(用 CTRL-C
来停止输出):
trace id f0f627 ip raw prerouting packet: iif "veth0" ether saddr ..
我们将在下一章详细分析该结果。如果你用的是 iptables,首先通过 iptables –version
命令检查一下已安装的版本。例如:
# iptables --version
iptables v1.8.5 (legacy)
(legacy)
意味着被跟踪的事件会被记录到内核的环形缓冲区中。你可以用 dmesg
或 journalctl
命令来查看这些事件。这些调试输出缺少一些信息,但和新工具提供的输出从概念上来讲很类似。你将需要首先查看规则被记录下来的行号,并与活跃的 iptables 规则集合手动关联。如果输出显示 (nf_tables)
,你可以使用 xtables-monitor
工具:
# xtables-monitor --trace
如果上述命令仅显示版本号,你仍然需要查看 dmesg
/journalctl
的输出。xtables-monitor
工具和 nft
监控跟踪工具使用相同的内核接口。它们之间唯一的不同点就是,xtables-monitor
工具会用 iptables
的语法打印事件,且如果你同时使用了 iptables-nft
和 nft
,它将不能打印那些使用了 maps/sets 或其他只有 nftables 才支持的功能的规则。
示例
我们假设需要调试一个到虚拟机/容器的端口不通的问题。ssh -p 1222 10.1.2.3
命令应该可以远程连接那台服务器上的某个容器,但连接请求超时了。
你拥有运行那台容器的主机的登录权限。现在登录该机器并增加一条跟踪规则。可通过前述案例查看如何增加一个临时的调试规则表。跟踪规则类似于这样:
nft "insert rule inet trace_debug trace_pre ip daddr 10.1.2.3 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1"
在添加完上述规则后,运行 nft monitor trace
,在跟踪模式下启动 nft,然后重试刚才失败的 ssh
命令。如果规则集较大,会出现大量的输出。不用担心这些输出,下一节我们会做逐行分析。
trace id 9c01f8 inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
trace id 9c01f8 inet trace_debug trace_pre rule ip daddr 10.2.1.2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue)
trace id 9c01f8 inet trace_debug trace_pre verdict continue
trace id 9c01f8 inet trace_debug trace_pre policy accept
trace id 9c01f8 inet nat prerouting packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
trace id 9c01f8 inet nat prerouting rule ip daddr 10.1.2.3 tcp dport 1222 dnat ip to 192.168.70.10:22 (verdict accept)
trace id 9c01f8 inet filter forward packet: iif "enp0" oif "veth21" ether saddr .. ip daddr 192.168.70.10 .. tcp dport 22 tcp flags == syn tcp window 29200
trace id 9c01f8 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop)
trace id 20a4ef inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
对跟踪结果作逐行分析
输出结果的第一行是触发后续输出的报文编号。这一行的语法与 nft 规则语法相同,同时还包括了接收报文的首部字段信息。你也可以在这一行找到接收报文的接口名称(此处为 enp0
)、报文的源和目的 MAC 地址、报文的源 IP 地址(可能很重要 - 报告问题的人可能选择了一个错误的或非预期的主机),以及 TCP 的源和目的端口。同时你也可以在这一行的开头看到一个“跟踪编号”。该编号标识了匹配跟踪规则的特定报文。第二行包括了该报文匹配的第一条跟踪规则:
trace id 9c01f8 inet trace_debug trace_pre rule ip daddr 10.2.1.2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue)
这就是刚添加的跟踪规则。这里显示的第一条规则总是激活报文跟踪的规则。如果在这之前还有其他规则,它们将不会在这里显示。如果没有任何跟踪输出结果,说明没有抵达这条跟踪规则,或者没有匹配成功。下面的两行表明没有后续的匹配规则,且 trace_pre
钩子允许报文继续传输(判定为接受)。
下一条匹配规则是:
trace id 9c01f8 inet nat prerouting rule ip daddr 10.1.2.3 tcp dport 1222 dnat ip to 192.168.70.10:22 (verdict accept)
这条 DNAT 规则设置了一个到其他地址和端口的映射。规则中的参数 192.168.70.10
是需要收包的虚拟机的地址,目前为止没有问题。如果它不是正确的虚拟机地址,说明地址输入错误,或者匹配了错误的 NAT 规则。
IP 转发
通过下面的输出我们可以看到,IP 路由引擎告诉 IP 协议栈,该报文需要被转发到另一个主机:
trace id 9c01f8 inet filter forward packet: iif "enp0" oif "veth21" ether saddr .. ip daddr 192.168.70.10 .. tcp dport 22 tcp flags == syn tcp window 29200
这是接收到的报文的另一种呈现形式,但和之前相比有一些有趣的不同。现在的结果有了一个输出接口集合。这在之前不存在的,因为之前的规则是在路由决策之前(prerouting
钩子)。跟踪编号和之前一样,因此仍然是相同的报文,但目标地址和端口已经被修改。假设现在还有匹配 tcp dport 1222
的规则,它们将不会对现阶段的报文产生任何影响了。
如果该行不包含输出接口(oif
),说明路由决策将报文路由到了本机。对路由过程的调试属于另外一个主题,本文不再涉及。
trace id 9c01f8 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
这条输出表明,报文匹配到了一个跳转到 allowed_dnats
链的规则。下一行则说明了连接失败的根本原因:
trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop)
这条规则无条件地将报文丢弃,因此后续没有关于该报文的日志输出。下一行则是另一个报文的输出结果了:
trace id 20a4ef inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr 10.2.1.2 ip daddr 10.1.2.3 ip protocol tcp tcp dport 1222 tcp flags == syn
跟踪编号已经和之前不一样,然后报文的内容却和之前是一样的。这是一个重传尝试:第一个报文被丢弃了,因此 TCP 尝试了重传。可以忽略掉剩余的输出结果了,因为它并没有提供新的信息。现在是时候检查那条链了。
规则集合分析
上一节我们发现报文在 inet filter 表中的一个名叫 allowed_dnats
的链中被丢弃。现在我们来查看它:
# nft list chain inet filter allowed_dnats
table inet filter {
chain allowed_dnats {
meta nfproto ipv4 ip daddr . tcp dport @allow_in accept
drop
}
}
接受 @allow_in
集的数据包的规则没有显示在跟踪日志中。我们通过列出元素的方式,再次检查上述报文的目标地址是否在 @allow_in
集中:
# nft "get element inet filter allow_in { 192.168.70.10 . 22 }"
Error: Could not process rule: No such file or directory
不出所料,地址-服务对并没有出现在集合中。我们将其添加到集合中。
# nft "add element inet filter allow_in { 192.168.70.10 . 22 }"
现在运行查询命令,它将返回新添加的元素。
# nft "get element inet filter allow_in { 192.168.70.10 . 22 }"
table inet filter {
set allow_in {
type ipv4_addr . inet_service
elements = { 192.168.70.10 . 22 }
}
}
ssh
命令现在应该可以工作,且跟踪结果可以反映出该变化:
trace id 497abf58 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
trace id 497abf58 inet filter allowed_dnats rule meta nfproto ipv4 ip daddr . tcp dport @allow_in accept (verdict accept)
trace id 497abf58 ip postrouting packet: iif "enp0" oif "veth21" ether .. trace id 497abf58 ip postrouting policy accept
这表明报文通过了转发路径中的最后一个钩子 - postrouting
。
如果现在仍然无法连接,问题可能处在报文流程的后续阶段,有可能并不在 nftables 的规则集合范围之内。
总结
本文介绍了如何通过 nftables 的跟踪机制检查丢包或其他类型的连接问题。本系列的下一篇文章将展示如何检查连接跟踪系统和可能与连接跟踪流相关的 NAT 信息。
使用iptables trace跟踪iptables流量
来源 https://blog.csdn.net/qq_43684922/article/details/126815699
使用LOG模块在任意位置打印报文信息
#在/etc/rsyslog.conf添加
kern.=debug /var/log/kern.debug.log
可能需要手动创建文件/var/log/kern.debug.log
重启rsyslog:
touch /var/log/kern.debug.log
systemctl restart rsyslog
之后可以添加如下的iptables规则,当报文到达该规则时,会在/var/log/kern.debug.log中打印出日志:
iptables -t raw -A OUTPUT -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "raw out: "
打印出来的日志如下:
Jun 13 19:58:21 dev-slave-110 kernel: raw prerouting: IN=eth0 OUT= MAC=52:54:15:5d:39:58:02:54:d4:90:3a:57:08:00 SRC=8.8.8.8 DST=10.39.0.110 LEN=84 TOS=0x00 PREC=0x00 TTL=32 ID=0 PROTO=ICMP TYPE=0 CODE=0 ID=13629 SEQ=15
和其它iptables一样,也可以添加IP、端口等条件:
iptables -t raw -A PREROUTING -p icmp -s 8.8.8.8/32 -m limit --limit 500/minute -j LOG --log-level 7 --log-prefix "mangle prerouting: "
全部加上
iptables -t raw -A OUTPUT -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "raw out: "
iptables -t mangle -A OUTPUT -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "mangle out: "
iptables -t nat -A OUTPUT -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "nat out: "
iptables -t filter -A OUTPUT -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "filter out: "
iptables -t mangle -A POSTROUTING -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "mangle post: "
iptables -t nat -A POSTROUTING -m limit --limit 5000/minute -j LOG --log-level 7 --log-prefix "nat post: "
注意这里设置了–limit防止打印出太多的日志,–limit如果设置的太小,可能会恰好丢弃要观察的包的日志
执行命令“ping -c 1 3.3.3.3”,然后查看iptables的日志记录:
cat /var/log/kern.debug.log |grep 3.3.3.3
Mar 31 05:48:29 compile kernel: raw out: IN= OUT=eth0 SRC=10.0.2.15 DST=3.3.3.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=29101 DF PROTO=ICMP TYPE=8 CODE=0 ID=4232 SEQ=1
Mar 31 05:48:29 compile kernel: mangle out: IN= OUT=eth0 SRC=10.0.2.15 DST=3.3.3.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=29101 DF PROTO=ICMP TYPE=8 CODE=0 ID=4232 SEQ=1
Mar 31 05:48:29 compile kernel: nat out: IN= OUT=eth0 SRC=10.0.2.15 DST=3.3.3.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=29101 DF PROTO=ICMP TYPE=8 CODE=0 ID=4232 SEQ=1
Mar 31 05:48:29 compile kernel: filter out: IN= OUT=eth0 SRC=10.0.2.15 DST=3.3.3.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=29101 DF PROTO=ICMP TYPE=8 CODE=0 ID=4232 SEQ=1
Mar 31 05:48:29 compile kernel: mangle post: IN= OUT=eth0 SRC=10.0.2.15 DST=3.3.3.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=29101 DF PROTO=ICMP TYPE=8 CODE=0 ID=4232 SEQ=1
Mar 31 05:48:29 compile kernel: nat post: IN= OUT=eth0 SRC=10.0.2.15 DST=3.3.3.3 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=29101 DF PROTO=ICMP TYPE=8 CODE=0 ID=4232 SEQ=1
从日志中可以看到,报文依次经过了raw out、mangle out、nat out、filter out、mangle out、nat post。
设置一条规则,在filter阶段丢弃到1.1.1.1的报文:
iptables -t filter -A OUTPUT -d 1.1.1.1 -j DROP
执行命令ping -c 1.1.1.1后,查看日志
$cat /var/log/kern.debug.log |grep 1.1.1.1
Mar 31 06:10:02 compile kernel: raw out: IN= OUT=eth0 SRC=10.0.2.15 DST=1.1.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=24260 DF PROTO=ICMP TYPE=8 CODE=0 ID=4426 SEQ=1
Mar 31 06:10:02 compile kernel: mangle out: IN= OUT=eth0 SRC=10.0.2.15 DST=1.1.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=24260 DF PROTO=ICMP TYPE=8 CODE=0 ID=4426 SEQ=1
Mar 31 06:10:02 compile kernel: nat out: IN= OUT=eth0 SRC=10.0.2.15 DST=1.1.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=24260 DF PROTO=ICMP TYPE=8 CODE=0 ID=4426 SEQ=1
Mar 31 06:10:02 compile kernel: filter out: IN= OUT=eth0 SRC=10.0.2.15 DST=1.1.1.1 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=24260 DF PROTO=ICMP TYPE=8 CODE=0 ID=4426 SEQ=1
从日志中可以看到,发送到1.1.1.1的报文到达filter表的OUTPUT规则后就终止了。
设置一条规则nat规则:
iptables -t nat -A OUTPUT -d 2.2.2.2 -j DNAT --to-destination 8.8.8.8
`ping -c 2.2.2.2`之后,可以看到下面的日志:
$cat /var/log/kern.debug.log |grep 2.2.2.2
Mar 31 06:12:38 compile kernel: raw out: IN= OUT=eth0 SRC=10.0.2.15 DST=2.2.2.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=2791 DF PROTO=ICMP TYPE=8 CODE=0 ID=4431 SEQ=1
Mar 31 06:12:38 compile kernel: mangle out: IN= OUT=eth0 SRC=10.0.2.15 DST=2.2.2.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=2791 DF PROTO=ICMP TYPE=8 CODE=0 ID=4431 SEQ=1
Mar 31 06:12:38 compile kernel: nat out: IN= OUT=eth0 SRC=10.0.2.15 DST=2.2.2.2 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=2791 DF PROTO=ICMP TYPE=8 CODE=0 ID=4431 SEQ=1
$ cat /var/log/kern.debug.log |grep 8.8.8.8
Mar 31 06:12:38 compile kernel: filter out: IN= OUT=eth0 SRC=10.0.2.15 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=2791 DF PROTO=ICMP TYPE=8 CODE=0 ID=4431 SEQ=1
Mar 31 06:12:38 compile kernel: mangle post: IN= OUT=eth0 SRC=10.0.2.15 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=2791 DF PROTO=ICMP TYPE=8 CODE=0 ID=4431 SEQ=1
Mar 31 06:12:38 compile kernel: nat post: IN= OUT=eth0 SRC=10.0.2.15 DST=8.8.8.8 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=2791 DF PROTO=ICMP TYPE=8 CODE=0 ID=4431 SEQ=1
从日志中可以看到,报文经过nat表的OUTPUT规则时进行了DNAT,然后使用新的DST地址经过filter的OUTPUT规则直到发送出。
使用TRACE模块对报文进行全程跟踪
TRACE只能在raw表中使用
通过-j TRACE
iptables -t raw -A PREROUTING -p icmp -s 8.8.8.8/32 -j TRACE
在man iptables-extensions中可以找到对TRACE的介绍:
This target marks packets so that the kernel will log every rule which match
the packets as those traverse the tables, chains, rules.
TRACE模块会在符合规则的报文上打上标记,将该报文经过的每一条规则打印出来,很方便的对报文做全程跟踪。TRACE模块只能在raw表中使用,还需要加载内核模块:
modprobe ipt_LOG ip6t_LOG nfnetlink_log
之后可以通过dmesg,或者在/var/log/message中查看到匹配的报文的日志:
Jun 16 17:44:05 dev-slave-110 kernel: TRACE: raw:PREROUTING:rule:2 IN=eth0 OUT= MAC=52:54:15:5d:39:58:02:54:d4:90:3a:57:08:00 SRC=8.8.8.8 DST=10.39.0.110 LEN=84 TOS=0x00 PREC=0x00 TTL=32 ID=0 PROTO=ICMP TYPE=0 CODE=0 ID=4064 SEQ=24
Jun 16 17:44:05 dev-slave-110 kernel: TRACE: raw:cali-PREROUTING:rule:1 IN=eth0 OUT= MAC=52:54:15:5d:39:58:02:54:d4:90:3a:57:08:00 SRC=8.8.8.8 DST=10.39.0.110 LEN=84 TOS=0x00 PREC=0x00 TTL=32 ID=0 PROTO=ICMP TYPE=0 CODE=0 ID=4064 SEQ=24
Jun 16 17:44:05 dev-slave-110 kernel: TRACE: raw:cali-PREROUTING:rule:3 IN=eth0 OUT= MAC=52:54:15:5d:39:58:02:54:d4:90:3a:57:08:00 SRC=8.8.8.8 DST=10.39.0.110 LEN=84 TOS=0x00 PREC=0x00 TTL=32 ID=0 PROTO=ICMP TYPE=0 CODE=0 ID=4064 SEQ=24
Jun 16 17:44:05 dev-slave-110 kernel: TRACE: raw:cali-from-host-endpoint:return:1 IN=eth0 OUT= MAC=52:54:15:5d:39:58:02:54:d4:90:3a:57:08:00 SRC=8.8.8.8 DST=10.39.0.110 LEN=84 TOS=0x00 PREC=0x00 TTL=32 ID=0 PROTO=ICMP TYPE=0 CODE=0 ID=4064 SEQ=24
Jun 16 17:44:05 dev-slave-110 kernel: TRACE: raw:cali-PREROUTING:return:5 IN=eth0 OUT= MAC=52:54:15:5d:39:58:02:54:d4:90:3a:57:08:00 SRC=8.8.8.8 DST=10.39.0.110 LEN=84 TOS=0x00 PREC=0x00 TTL=32 ID=0 PROTO=ICMP TYPE=0 CODE=0 ID=4064 SEQ=24
添加与删除
iptables -t raw -I <OUTPUT|PREROUTING> <filters> -j TRACE
iptables -t raw -D <OUTPUT|PREROUTING> <filters> -j TRACE
# iptables -t raw -A OUTPUT -p icmp -j TRACE
# iptables -t raw -A PREROUTING -p icmp -j TRACE
Trace incoming packets(跟踪入向包)
# iptables -t raw -A PREROUTING -p tcp --destination 1.2.3.4 --dport 443 -j TRACE
# sudo iptables -t raw -L -v -n --line-numbers
Chain PREROUTING (policy ACCEPT 3501 packets, 946K bytes)
num pkts bytes target prot opt in out source destination
1 468 28159 TRACE tcp -- * * 0.0.0.0/0 1.2.3.4 tcp dpt:443
Chain OUTPUT (policy ACCEPT 885 packets, 695K bytes)
num pkts bytes target prot opt in out source destination
Trace to the internal network will look like this.
[...]
Jul 18 18:33:27 cerberus kernel: [68907.892027] TRACE: raw:PREROUTING:policy:2 IN=eth0 OUT= MAC=00:15:17:c3:a1:aa:00:15:17:c3:fb:07:01:00 SRC=172.69.63.16 DST=1.2.3.4 LEN=40 TOS=0x00 PREC=0x00 TTL=56 ID=64783 DF PROTO=TCP SPT=62598 DPT=443 SEQ=234589096 ACK=404477568 WINDOW=82 RES=0x00 ACK URGP=0
Jul 18 18:33:27 cerberus kernel: [68907.892093] TRACE: mangle:INPUT:policy:1 IN=eth0 OUT= MAC=00:15:17:c3:a1:aa:00:15:17:c3:fb:07:01:00 SRC=172.69.63.16 DST=1.2.3.4 LEN=40 TOS=0x00 PREC=0x00 TTL=56 ID=64783 DF PROTO=TCP SPT=62598 DPT=443 SEQ=234589096 ACK=404477568 WINDOW=82 RES=0x00 ACK URGP=0
Jul 18 18:33:27 cerberus kernel: [68907.892113] TRACE: filter:INPUT:rule:6 IN=eth0 OUT= MAC=00:15:17:c3:a1:aa:00:15:17:c3:fb:07:01:00 SRC=172.69.63.16 DST=1.2.3.4 LEN=40 TOS=0x00 PREC=0x00 TTL=56 ID=64783 DF PROTO=TCP SPT=62598 DPT=443 SEQ=234589096 ACK=404477568 WINDOW=82 RES=0x00 ACK URGP=0
Jul 18 18:33:27 cerberus kernel: [68907.892150] TRACE: raw:PREROUTING:policy:2 IN=eth0 OUT= MAC=00:15:17:c3:a1:aa:00:15:17:c3:fb:07:01:00 SRC=172.69.63.16 DST=1.2.3.4 LEN=40 TOS=0x00 PREC=0x00 TTL=56 ID=64784 DF PROTO=TCP SPT=62598 DPT=443 SEQ=234589096 ACK=404477569 WINDOW=82 RES=0x00 ACK RST URGP=0
[...]
Trace outgoing packets(跟踪出向包)
Use raw table and OUTPUT to trace locally generated packets.
$ sudo iptables -t raw -A OUTPUT -p tcp --destination 8.8.8.8 --dport 53 -j TRACE
$ sudo iptables -t raw -A OUTPUT -p udp --destination 8.8.8.8 --dport 53 -j TRACE
Inspect raw table.
$ sudo iptables -t raw -L -v -n --line-numbers
Chain PREROUTING (policy ACCEPT 1281 packets, 422K bytes)
num pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 379 packets, 324K bytes)
num pkts bytes target prot opt in out source destination
1 0 0 TRACE tcp -- * * 0.0.0.0/0 8.8.8.8 tcp dpt:53
2 0 0 TRACE udp -- * * 0.0.0.0/0 8.8.8.8 udp dpt:53
Trace to the external DNS server will look like this.
[...]
Jul 18 18:48:38 cerberus kernel: [69819.286907] TRACE: raw:OUTPUT:policy:3 IN= OUT=eth0 SRC=1.2.3.4 DST=8.8.8.8 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=27373 PROTO=UDP SPT=45407 DPT=53 LEN=58 UID=2018 GID=2018
Jul 18 18:48:38 cerberus kernel: [69819.286922] TRACE: nat:OUTPUT:policy:1 IN= OUT=eth0 SRC=1.2.3.4 DST=8.8.8.8 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=27373 PROTO=UDP SPT=45407 DPT=53 LEN=58 UID=2018 GID=2018
Jul 18 18:48:38 cerberus kernel: [69819.286929] TRACE: filter:OUTPUT:rule:7 IN= OUT=eth0 SRC=1.2.3.4 DST=8.8.8.8 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=27373 PROTO=UDP SPT=45407 DPT=53 LEN=58 UID=2018 GID=2018
Jul 18 18:48:38 cerberus kernel: [69819.286939] TRACE: nat:POSTROUTING:policy:2 IN= OUT=eth0 SRC=1.2.3.4 DST=8.8.8.8 LEN=78 TOS=0x00 PREC=0x00 TTL=64 ID=27373 PROTO=UDP SPT=45407 DPT=53 LEN=58 UID=2018 GID=2018
[...]
iptables自身提供了TRACE功能,一旦设定,当数据包匹配到任意chain上任意table的处理规则时,iptables会在系统日志(/var/log/syslog)中自动输出此时的数据包状态日志。
ebtables的调试设置
我们的重点在iptables,为ebtables只是辅助,帮助我们看清数据包到底是在哪一层被hook进iptables的规则链中进行处理的。因此我们在全图中的每个ebtables的built-in chain上都加上LOG(ebtables目前还不支持TRACE):
# ebtables -t broute -A BROUTING -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:broute:BROUTING" -j ACCEPT
# ebtables -t nat -A OUTPUT -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:nat:OUTPUT" -j ACCEPT
# ebtables -t nat -A PREROUTING -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:nat:PREROUTING" -j ACCEPT
# ebtables -t filter -A INPUT -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:filter:INPUT" -j ACCEPT
# ebtables -t filter -A FORWARD -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:filter:FORWARD" -j ACCEPT
# ebtables -t filter -A OUTPUT -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:filter:OUTPUT" -j ACCEPT
# ebtables -t nat -A POSTROUTING -p ipv4 --ip-proto 1 --log-level 6 --log-ip --log-prefix "TRACE: eb:nat:POSTROUTING" -j ACCEPT
注意:这里--ip-proto 1 表示仅match icmp packet。
The Internet Protocol version 4 (IPv4) (RFC791) there is a field, called "Protocol", to identify the next level protocol. This is an 8 bit field. In Internet Protocol version 6 (IPv6) (RFC1883 and RFC2460) this field is called the "Next Header" field.
Protocol Numbers 0 HOPOPT IPv6 Hop-by-Hop Option 1 ICMP Internet Control Message 2 IGMP Internet Group Management 3 GGP Gateway-to-Gateway 4 IP IP in IP (encapsulation) 5 ST Stream 6 TCP Transmission Control 7 CBT CBT 8 EGP Exterior Gateway Protocol 9 IGP any private interior gateway (used by Cisco for their IGRP) 10 BBN-RCC-MON BBN RCC Monitoring 11 NVP-II Network Voice Protocol 12 PUP PUP 13 ARGUS ARGUS 14 EMCON EMCON 15 XNET Cross Net Debugger 16 CHAOS Chaos 17 UDP User Datagram 18 MUX Multiplexing 19 DCN-MEAS DCN Measurement Subsystems 20 HMP Host Monitoring 21 PRM Packet Radio Measurement 22 XNS-IDP XEROX NS IDP 23 TRUNK-1 Trunk-1 24 TRUNK-2 Trunk-2 25 LEAF-1 Leaf-1 26 LEAF-2 Leaf-2 27 RDP Reliable Data Protocol 28 IRTP Internet Reliable Transaction 29 ISO-TP4 ISO Transport Protocol Class 4 30 NETBLT Bulk Data Transfer Protocol 31 MFE-NSP MFE Network Services Protocol 32 MERIT-INP MERIT Internodal Protocol 33 SEP Sequential Exchange Protocol 34 3PC Third Party Connect Protocol 35 IDPR Inter-Domain Policy Routing Protocol 36 XTP XTP 37 DDP Datagram Delivery Protocol 38 IDPR-CMTP IDPR Control Message Transport Proto 39 TP++ TP++ Transport Protocol 40 IL IL Transport Protocol 41 IPv6 Ipv6 42 SDRP Source Demand Routing Protocol 43 IPv6-Route Routing Header for IPv6 44 IPv6-Frag Fragment Header for IPv6 45 IDRP Inter-Domain Routing Protocol 46 RSVP Reservation Protocol 47 GRE General Routing Encapsulation 48 MHRP Mobile Host Routing Protocol 49 BNA BNA 50 ESP Encapsulating Security Payload 51 AH Authentication Header 52 I-NLSP Integrated Net Layer Security TUBA 53 SWIPE IP with Encryption 54 NARP NBMA Address Resolution Protocol 55 MOBILE IP Mobility 56 TLSP Transport Layer Security Protocol using Kryptonet key management 57 SKIP SKIP 58 IPv6-ICMP ICMP for IPv6 59 IPv6-NoNxt No Next Header for IPv6 60 IPv6-Opts Destination Options for IPv6 61 any host internal protocol 62 CFTP CFTP 63 any local network 64 SAT-EXPAK SATNET and Backroom EXPAK 65 KRYPTOLAN Kryptolan 66 RVD MIT Remote Virtual Disk Protocol 67 IPPC Internet Pluribus Packet Core 68 any distributed file system 69 SAT-MON SATNET Monitoring 70 VISA VISA Protocol 71 IPCV Internet Packet Core Utility 72 CPNX Computer Protocol Network Executive 73 CPHB Computer Protocol Heart Beat 74 WSN Wang Span Network 75 PVP Packet Video Protocol 76 BR-SAT-MON Backroom SATNET Monitoring 77 SUN-ND SUN ND PROTOCOL-Temporary 78 WB-MON WIDEBAND Monitoring 79 WB-EXPAK WIDEBAND EXPAK 80 ISO-IP ISO Internet Protocol 81 VMTP VMTP 82 SECURE-VMTP SECURE-VMTP 83 VINES VINES 84 TTP TTP 85 NSFNET-IGP NSFNET-IGP 86 DGP Dissimilar Gateway Protocol 87 TCF TCF 88 EIGRP EIGRP 89 OSPFIGP OSPFIGP 90 Sprite-RPC Sprite RPC Protocol 91 LARP Locus Address Resolution Protocol 92 MTP Multicast Transport Protocol 93 AX.25 AX.25 Frames 94 IPIP IP-within-IP Encapsulation Protocol 95 MICP Mobile Internetworking Control Protocol 96 SCC-SP Semaphore Communications Sec. Protocol 97 ETHERIP Ethernet-within-IP Encapsulation 98 ENCAP Encapsulation Header 99 any private encryption scheme 100 GMTP GMTP 101 IFMP Ipsilon Flow Management Protocol 102 PNNI PNNI over IP 103 PIM Protocol Independent Multicast 104 ARIS ARIS 105 SCPS SCPS 106 QNX QNX 107 A/N Active Networks 108 IPComp IP Payload Compression Protocol 109 SNP Sitara Networks Protocol 110 Compaq-Peer Compaq Peer Protocol 111 IPX-in-IP IPX in IP 112 VRRP Virtual Router Redundancy Protocol 113 PGM PGM Reliable Transport Protocol 114 any 0-hop protocol 115 L2TP Layer Two Tunneling Protocol 116 DDX D-II Data Exchange (DDX) 117 IATP Interactive Agent Transfer Protocol 118 STP Schedule Transfer Protocol 119 SRP SpectraLink Radio Protocol 120 UTI UTI 121 SMP Simple Message Protocol 122 SM SM 123 PTP Performance Transparency Protocol 124 ISIS over IPv4 125 FIRE 126 CRTP Combat Radio Transport Protocol 127 CRUDP Combat Radio User Datagram 128 SSCOPMCE 129 IPLT 130 SPS Secure Packet Shield 131 PIPE Private IP Encapsulation within IP 132 SCTP Stream Control Transmission Protocol 133 FC Fibre Channel 134 RSVP-E2E-IGNORE 135-254 Unassigned 255 Reserved
================= End