为什么访问本地端口时要经过netfilter postrouting?

为什么访问本地端口时要经过netfilter postrouting?

环境:

[root@VM-32-4-centos ~]# uname -r
3.10.0-514.26.2.el7.x86_64

我在以下链中设置了日志打印:

[root@VM-32-4-centos ~]# iptables -A INPUT -p tcp --dport 8000 -j LOG --log-prefix "INPUT DPORT 8000 LOGS: "
[root@VM-32-4-centos ~]# iptables -A INPUT -p tcp --sport 8000 -j LOG --log-prefix "INPUT SPORT 8000 LOGS: "
[root@VM-32-4-centos ~]# iptables -A OUTPUT -p tcp --dport 8000 -j LOG --log-prefix "OUTPUT DPORT 8000 LOGS: "
[root@VM-32-4-centos ~]# iptables -A OUTPUT -p tcp --sport 8000 -j LOG --log-prefix "OUTPUT SPORT 8000 LOGS: "
[root@VM-32-4-centos ~]# iptables -t nat -A POSTROUTING -p tcp --sport 8000 -j LOG --log-prefix "POSTROUTING SPORT 8000 LOGS: "
[root@VM-32-4-centos ~]# iptables -t nat -A POSTROUTING -p tcp --dport 8000 -j LOG --log-prefix "POSTROUTING DPORT 8000 LOGS: "

然后我执行telnet命令:

telnet 127.0.0.1 8000

tail -f /var/log/message,显示以下内容:

May  2 23:54:47 VM-32-4-centos kernel: OUTPUT DPORT 8000 LOGS: IN= OUT=lo SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x10 PREC=0x00 TTL=64 ID=6163 DF PROTO=TCP SPT=51096 DPT=8000 WINDOW=43690 RES=0x00 SYN URGP=0 
May  2 23:54:47 VM-32-4-centos kernel: POSTROUTING DPORT 8000 LOGS: IN= OUT=lo SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x10 PREC=0x00 TTL=64 ID=6163 DF PROTO=TCP SPT=51096 DPT=8000 WINDOW=43690 RES=0x00 SYN URGP=0 
May  2 23:54:47 VM-32-4-centos kernel: INPUT DPORT 8000 LOGS: IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x10 PREC=0x00 TTL=64 ID=6163 DF PROTO=TCP SPT=51096 DPT=8000 WINDOW=43690 RES=0x00 SYN URGP=0 
May  2 23:54:47 VM-32-4-centos kernel: OUTPUT SPORT 8000 LOGS: IN= OUT=lo SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=8000 DPT=51096 WINDOW=43690 RES=0x00 ACK SYN URGP=0 
May  2 23:54:47 VM-32-4-centos kernel: INPUT SPORT 8000 LOGS: IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=0 DF PROTO=TCP SPT=8000 DPT=51096 WINDOW=43690 RES=0x00 ACK SYN URGP=0 
May  2 23:54:47 VM-32-4-centos kernel: OUTPUT DPORT 8000 LOGS: IN= OUT=lo SRC=127.0.0.1 DST=127.0.0.1 LEN=52 TOS=0x10 PREC=0x00 TTL=64 ID=6164 DF PROTO=TCP SPT=51096 DPT=8000 WINDOW=342 RES=0x00 ACK URGP=0 
May  2 23:54:47 VM-32-4-centos kernel: INPUT DPORT 8000 LOGS: IN=lo OUT= MAC=00:00:00:00:00:00:00:00:00:00:00:00:08:00 SRC=127.0.0.1 DST=127.0.0.1 LEN=52 TOS=0x10 PREC=0x00 TTL=64 ID=6164 DF PROTO=TCP SPT=51096 DPT=8000 WINDOW=342 RES=0x00 ACK URGP=0 

请问,本地访问时,为什么SYN包要经过POSTROUTING?

后续的ACK+SYN数据包没有经过POSTROUTING,为什么?

请问为什么会这样,有相关资料吗?

谢谢。

答案1

下面是 Netfilter 和通用网络中的数据包流示意图,描述了数据包如何在 Linux 网络堆栈中移动(您可以忽略下面的蓝色链路层字段,它用于桥接而不是路由):

Netfilter 和通用网络中的数据包流

对于留在系统本地的数据包,路由堆栈必须对其进行评估才能使用lo(环回)接口:

# ip route get 127.0.0.1
local 127.0.0.1 dev lo src 127.0.0.1 uid 0 
    cache <local> 

所以它将遵循示意图中描述的流程

  • 由本地进程发出
  • 路由决策:输出接口lo
  • 各种链挂在输出中
  • POSTROUTING 中的各种链挂钩:转发或发出的数据包总是会发生这种情况
  • 最后发射到lo接口,原理图右侧

lo接口现在发挥了它的作用:环回。数据包通过接口环回lo并返回原理图的左侧...再次到达lo接口,但这次是传入,而不是传出。

  • “来自”lo接口的传入数据包
  • 在 PREROUTING 中挂钩的各种链:这总是发生在接收到的数据包上
  • 路由决策:目的地将是一个本地进程
  • INPUT 中的各种链
  • 由本地进程接收

现在有一个问题:NAT。 NAT 规则用于确定同一流中的所有其他数据包如何以相同的方式进行转换。一旦给定流发生 NAT,同一流的所有其他数据包(包括回复)将直接由 Netfilter 和 conntrack 处理(它会记住有关此流的所需信息,包括 NAT 的完成方式):它们不会遍历iptables不再了。在原理图中有一个关于它的注释:

仅针对“新”连接查阅“nat”表

然后,有关此流的 NAT 的所有其他内容都会被 Netfilter 短路,它不会调用iptables纳特不再挂钩了。


这允许使用 nat 表解释结果。使用两个 nat/POSTROUTING 规则:

  • 流的第一个数据包:初始查询(SYN 数据包)将遍历 NAT 规则

    • 过滤--dport器匹配并触发日志
    • --sport匹配(无日志)
  • 该流的任何后续数据包(包括来自端口 8080 的应用程序的回复)将不再遍历 nat 表,因为该流已经存在(即:该数据包不处于“NEW”状态)

    所以最终--sport规则将永远不会记录任何内容。

同样,如果在 nat/PREROUTING(或很少使用的 nat/INPUT)中也添加一条 LOG 规则,它不会显示来自接口的任何内容lo,因为本地流的第一个数据包总是被发出 lo收到之前 lo:这样的流程将始终存在。

如果您使用以下命令登录 mangle/POSTROUTING:

iptables -t mangle -A POSTROUTING -p tcp --sport 8000 -j LOG --log-prefix "mangle POSTROUTING SPORT 8000 LOGS: "
iptables -t mangle -A POSTROUTING -p tcp --dport 8000 -j LOG --log-prefix "mangle POSTROUTING DPORT 8000 LOGS: "

您将在日志中看到每个查询和每个回复数据包,就像过滤器/输入和过滤器/输出一样。

如果不是上面添加了一个仅查看新状态数据包的过滤器,就像总是发生的那样纳特桌子:

iptables -t mangle -A POSTROUTING -m conntrack --ctstate NEW -p tcp --sport 8000 -j LOG --log-prefix "mangle POSTROUTING SPORT 8000 LOGS: "
iptables -t mangle -A POSTROUTING -m conntrack --ctstate NEW -p tcp --dport 8000 -j LOG --log-prefix "mangle POSTROUTING DPORT 8000 LOGS: "

那么同样的初始行为会再次发生:每个流的第一个数据包只有一个日志(因此永远不会因为--sport它是回复,所以不是流的第一个数据包)。

相关内容