我的任务是为 Linux 内核编写一个 IPv4-IPv6 转换器 ( RFC 6146 )。简而言之,它是一个介于 IPv4 和 IPv6 网络之间的网关,并通过交换数据包头和掩码地址来实现它们之间的透明通信。
看到内核有 Netfilter,一个用于更改数据包的框架,我原本以为我可以编写一个 Netfilter 模块并从那里进行翻译。我可以拦截所有数据包,使用通常的 skb_pull/skb_push 操作调整它们的大小,覆盖一些字节,最后愉快地将它们返回给内核。
事实证明,切换协议对于 Netfilter 来说过于极端了。因为处理 IPv4 数据包的代码完全独立于处理 IPv6 数据包的代码,所以 Netfilter 模块之前和之后的代码都假定网络协议的标头仍然存在。因此,如果我将 IPv4 标头更改为 IPv6 标头,内核会发疯,因为它会继续读取标头,就好像它是 IPv4 标头一样。
(或者至少,这是我在阅读 net/ipv4/ip_input.c 后所相信的;在调用 NF_HOOK 之后内核做的第一件事就是在 ip_rcv_finish() 期间提取 IPv4 标头。)
我看到了第二种选择:从头开始推导出一个新的 sk_buff,让 Netfilter NF_DROP 原始数据包,然后以某种方式发送新数据包。
这就是我对内核原理和风格的有限熟悉使我停滞不前的地方:我想尽量减少对我的解决方案的皱眉,但是替换整个数据包而不是更改其标头似乎不自然、无效率,甚至是对 Netfilter 设计的亵渎。但我不能确定,我希望有经验的建议。另外,我认为没有其他选择。
问题:还有其他选择吗?最自然的方法是什么?
我想支持自 2.6 以来的所有内核版本。如果那是不可能的,那么更新更好。