Chion82 / netfilter-full-cone-nat

A kernel module to turn MASQUERADE into full cone SNAT
GNU General Public License v2.0
433 stars 123 forks source link

对于5元组没有变化的连接,前后两次SNAT出接口变化后,CT表无法及时删除失效的跟踪连接,导致新的NAT会话失败 #18

Open tingjunli opened 6 years ago

tingjunli commented 6 years ago

如: 源IP、源端口、协议、目的IP、目的端口、目的协议都不变化的情况下,只有路由后的出接口变化,使用被动老化机制时会导致旧连接(旧的接口IP)一直存在,新的出接口IP无法正常使用

Chion82 commented 6 years ago

这种情况是否出现在同五元组的流量一直持续,而中途路由出口变更的情况下? 本模块内的映射表存活状态理应不会影响到内核的 CT 表,可能是使用了模块内过期的映射表项导致的。 如果路由出口变更后,客户端停止流量,过几分钟后再使用同样的五元组发包,能否成功重新建立 NAT 会话?

tingjunli commented 6 years ago

是你说的这种情况下出现的问题(同五元组的流量一直持续,而中途路由出口变更的情况),客户端停止流量,过几分钟是可以重新建立会话的,但是如果客户端不停止流量,重新发起新的会话连接,也没有办法创建新的会话。我查看了full-cone源码和内核NAT源码,原因是因为full-cone在SNAT的时候,没有更新新会话的出接口ifindex,导致内核中的出接口检查失效,所以后面该5元组的会话都不会因为出接口的变化而失效,如下图 image

liangchen2233 commented 4 years ago

这个BUG好像还没修复

Chion82 commented 4 years ago

@liangchen2233 抱歉,之前测试环境坏了一直没有验证这个 pull request。能否测试一下 #19 是否解决了这个问题?

liangchen2233 commented 4 years ago

您好,没有解决

Chion82 commented 4 years ago

我在最近实际使用中还发现,不仅是出接口发生改变(路由改变)后没有自动跟踪到,出接口的 IP 发生改变后貌似也不能检测到,在流量一直有的情况下当前 CT 无法自动删除。

tingjunli commented 4 years ago

您好,没有解决

我自己测试的话,只是换了出接口,CT表是能够及时删除的,你可以把那个patch合入再验证看看

llccd commented 4 years ago

试试我改的 https://github.com/llccd/netfilter-full-cone-nat/tree/dev ,原理是把nat_mapping里面的ifindex改成出接口IP,同时合并@tingjunli的patch,这样出接口改变或出接口IP改变就应该都没问题了。 Update: 在fullconenat_tg_init里面调用一下nf_nat_masquerade_inet_register_notifiers好像就行了

taolu-soft commented 4 years ago

试试我改的 https://github.com/llccd/netfilter-full-cone-nat/tree/dev ,原理是把nat_mapping里面的ifindex改成出接口IP,同时合并@tingjunli的patch,这样出接口改变或出接口IP改变就应该都没问题了。 Update: 在fullconenat_tg_init里面调用一下nf_nat_masquerade_inet_register_notifiers好像就行了

不能,直接炸了

llccd commented 4 years ago

@taolu-soft 你的内核版本是多少,是模块加载不上还是运行崩溃了,能不能提供stacktrace?

另外,解决CT表无法及时删除的最简单方法是

  1. 添加赋值ifindex的patch
  2. 确保MASQUERADE模块已加载,或者MASQUERADE编译进内核(可以不添加MASQUERADE规则,但模块要加载)
Chion82 commented 4 years ago

我做了一个临时的 patch,大家可以再测试一下,在我的环境上是ok的。加上这个 patch 之后应该不需要再依赖 MASQUERADE 模块

https://gist.github.com/Chion82/9a6880edf4cd6e1993e22b5460bde869

llccd commented 4 years ago

这样好像就是将MASQUERADE模块里的notifier重新实现了一遍,如果MASQUERADE模块同时加载的话,相同代码的功能就会执行两遍。

另外,不依赖MASQUERADE是不可能的,如果你在编译内核时把MASQUERADE禁用,nat结构体里面根本就不会有masq_index这个成员。

我觉得使用下面的方法更好,调用下面两个函数可以直接注册和MASQUERADE模块相同的notifier,而且在与MASQUERADE模块同时加载时只会注册一次:

Chion82 commented 4 years ago

@llccd 是的,因为我的测试环境内核很老了,我在浏览 MASQUERADE 模块的源码时还没有这个公共函数,所以就把 MASQUERADE 中的部分代码做了一下版本兼容直接复制过来用了。这个只是临时的用于测试的补丁,尽可能保持简单,除了 notifier 没有修改别的东西,用来供各位测试是否解决了这个issue的。如果这个能够解决,之后会参考你的方法将更优雅的 fix 并入本仓库中。

我指的不依赖 MASQUERADE 模块指的是不需要在运行时加载 MASQUERADE 模块。

taolu-soft commented 4 years ago

@llccd 是的,因为我的测试环境内核很老了,我在浏览 MASQUERADE 模块的源码时还没有这个公共函数,所以就把 MASQUERADE 中的部分代码做了一下版本兼容直接复制过来用了。这个只是临时的用于测试的补丁,尽可能保持简单,除了 notifier 没有修改别的东西,用来供各位测试是否解决了这个issue的。如果这个能够解决,之后会参考你的方法将更优雅的 fix 并入本仓库中。

我指的不依赖 MASQUERADE 模块指的是不需要在运行时加载 MASQUERADE 模块。

/root/netfilter-full-cone-nat/xt_FULLCONENAT.c: 在函数‘fullconenat_device_event’中: /root/netfilter-full-cone-nat/xt_FULLCONENAT.c:743:15: 错误:提供给函数‘nf_ct_iterate_cleanup’的实参太少 (void *)(long)dev->ifindex);

Chion82 commented 4 years ago

@taolu-soft patch 更新修复了,请重新编译试一下。

taolu-soft commented 4 years ago

@taolu-soft patch 更新修复了,请重新编译试一下。 还是这样,内核版本:3.10.0-957.10.1.vz7.85.17 /root/netfilter-full-cone-nat/xt_FULLCONENAT.c: 在函数‘fullconenat_device_event’中: /root/netfilter-full-cone-nat/xt_FULLCONENAT.c:746:15: 错误:提供给函数‘nf_ct_iterate_cleanup’的实参太少 (void *)(long)dev->ifindex);

MeIsReallyBa commented 4 years ago

这样好像就是将MASQUERADE模块里的notifier重新实现了一遍,如果MASQUERADE模块同时加载的话,相同代码的功能就会执行两遍。

另外,不依赖MASQUERADE是不可能的,如果你在编译内核时把MASQUERADE禁用,nat结构体里面根本就不会有masq_index这个成员。

我觉得使用下面的方法更好,调用下面两个函数可以直接注册和MASQUERADE模块相同的notifier,而且在与MASQUERADE模块同时加载时只会注册一次:

  • 对于>=5.2版本的内核,可以使用nf_nat_masquerade_inet_register_notifiers(),
  • 对于<5.2且>=3.18版本的内核,可以使用nf_nat_masquerade_ipv4_register_notifier()
  • 对于3.18版本以下的内核,只能重新实现notifier或者加载MASQUERADE模块

void nf_nat_masquerade_ipv4_register_notifier(void) { / check if the notifier was already set / if (atomic_inc_return(&masquerade_notifier_refcount) > 1) return;

/* Register for device down reports */
register_netdevice_notifier(&masq_dev_notifier);
/* Register IP address change reports */
register_inetaddr_notifier(&masq_inet_notifier);

} EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv4_register_notifier);

这是4.x内核里的,貌似对3.18-5.2处理有误?