最近我不得不摆弄iptables
Go 的规则,我注意到码头工人的和核心的包装器库exec()
输出到iptables
命令并屏幕抓取标准输出。这让我感到惊讶。
在Python领域,有python iptables:
与 iptables 的互操作性是通过使用 iptables C 库(libiptc、libxtables 和 iptables 扩展)实现的,而不是调用 iptables 二进制文件并解析其输出。
我猜想 Python 库允许在运行时加载 C 库,而 Go 不能[编辑:我错了],但 Go 不能静态链接到这些库吗?因为 Go 库必须libxtables.a
从某个地方获取等等? (我正在Debian中dpkg -L
查找相关软件包,但我只看到了s。)-dev
.so
无论如何,根据网络过滤器常见问题解答:
4.5 是否有用于添加/删除规则的 C/C++ API?
不幸的是,答案是:不。
现在您可能会想“但是 libiptc 呢?”。正如邮件列表中多次指出的那样,libiptc 是绝不旨在用作公共接口。我们不保证稳定的接口,并且计划在 linux packet 的下一个版本中将其删除
我们很清楚这样的 API 存在根本性的缺陷,我们正在努力改善这种情况。在那之前,建议使用 system() 或打开一个管道进入 iptables-restore 的标准输入。后者将为您提供更好的性能过滤。 libiptc 太低层,无法合理使用。
好吧,也许您不应该将这些 C 库视为稳定的。然后我想“为什么不直接交谈/proc
或其他什么?”事实证明,iptables
大多数情况下不用于/proc
与内核对话(只是为了读取表名?),并且主接口实际上位于getsockopt()
未setsockopt()
绑定的套接字上。
这又是一个惊喜! (但也许只是因为我不熟悉这种代码,这似乎是一种与内核交互的有趣方式。)
iptables
我从到ipchains
到进行了一些考古学ipfw
,发现了一个1997 年的 ipfw 手册页这让我感觉好一点:
BUGS
The setsockopt(2) interface is a crock. This should be
put under /proc/sys/net/ipv4 and the world would be a bet-
ter place.
您可以在其他地方找到“The ipfw utility first comes in FreeBSD 2.0”和
HISTORY
Initially this utility was written for BSDI by:
Daniel Boulet <[email protected]>
The FreeBSD version is written completely by:
Ugen J.S.Antsilevich <[email protected]>
while synopsis partially compatible with old one.
ipfw
这是1994 年的 FreeBSD 2.0 版本。它仍然使用setsockopt()
,但似乎使用kvm_read()
而不是getsockopt()
。
我的问题
- 上面说的不对的地方请指正:)
{get,set}sockopt()
他们最初为什么选择?这是不常见的,还是对我来说是新的?- 为什么在某个时候没有改变?就像
/proc
1997 年手册页所建议的那样。 - 为什么他们从未提出一个稳定/公共的 C 接口
iptables
?
我的后续问题
“不完全是,主要接口是netlink的NETLINK_NETFILTER家族”
感谢这位指点!
我正在看https://git.netfilter.org/iptables/tree并且似乎只libipq
使用了 netlink 套接字,除非我遗漏了一些东西。
$ ick "socket\("
include/libiptc/libiptc.h
158:int iptc_get_raw_socket(void);
include/libiptc/libip6tc.h
152:int ip6tc_get_raw_socket(void);
utils/nfsynproxy.c
129: fd = socket(AF_INET, SOCK_STREAM, 0);
libiptc/libiptc.c
1312: sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
extensions/libxt_set.h
14: int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
libipq/libipq.c
223: h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
225: h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
libxtables/xtables.c
881: sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
这些libipq
东西对我来说看起来“正常”:创建一个套接字,用 sockaddr、 、 等绑定sendto()
它recvfrom()
。
似乎还xtables-monitor
使用一些mnl_socket_*
包装器来完成网络链接的工作。
但我无法弄清楚libiptc
-xtables
没有提到 netlink,而且它们甚至没有绑定套接字。
https://git.netfilter.org/iptables/tree/libxtables/xtables.c#n881
https://git.netfilter.org/iptables/tree/libiptc/libiptc.c#n1312
还有其他示例不(似乎?)使用 netlink,例如来自 mozilla rr 调试器的代码:https://github.com/mozilla/rr/blob/master/src/test/netfilter.c
从 2000 年(2.2)开始,netlink 只适用于 Linux,而这个{get,set}sockopt()
东西(至少)ipfw
更老,而且是在 BSD 领域。
bind()
我不太确定我的问题是什么了:)非netlink套接字如何在没有sockaddr的情况下以某种方式与正确的内核端进行通信?
答案1
我猜Python库允许在运行时加载C库,而Go不能
去能使用共享 C 库。
事实证明,
iptables
大多数情况下不用于/proc
与内核对话(只是读取表名?),并且主接口实际上是getsockopt()
和setsockopt()
。
{get,set}sockopt()
他们最初为什么选择?这是不常见的,还是对我来说是新的?
(这个答案之前错误地指出,主界面是netlink
的NETLINK_NETFILTER
家人。)
你是对的,iptables
使用对getsockopt
适当类型的未绑定套接字的调用来与内核交互,最终调用内核的netfilter
代码。我怀疑这是历史原因......
顺便说一句,getsockopt
并且setsockopt
在各种状态下的套接字上工作,包括未绑定,并且它们必须这样做,因为它们用于在连接之前设置选项。 -via-netfilter
连接sockopt
实际上是一个侧通道。
为什么在某个时候没有改变?就像
/proc
1997 年手册页所建议的那样。
原因可能有很多,但毫无疑问的是向后兼容性——在 Linux 中,用户空间界面永远不应该被破坏。因此,虽然可以提供新的界面,但现有的界面仍将保留,这减少了改进界面或重写已运行程序的动力。
只要iptables
命令行工具可以不费力气地使用,而提供更好的界面需要更多的努力,改变就会很困难。适当维护的libiptc
风格库也是可能的,但开发人员从来没有太多动力iptables
这样做。
为什么他们从未提出一个稳定/公共的 C 接口
iptables
?
因为“他们”从来没有抽出时间来解决这个问题。现在iptables
正在逐步淘汰nftables
,其中包括许多提供不同抽象级别访问的库(libnftables
、libnftnl
、libnfnetlink
)。它使用netlink
,它被设计为基于套接字的接口,用于在内核和用户空间之间传输信息,并且非常适合nftables
( 和iptables
) 等工具。该netlink
文档列出了其他可用的系列;这些涵盖了相当多的内核托管功能。netlink
。
你可能会发现https://github.com/vishvananda/netlinkiptables
对于从 Go开始驾驶很有用。 (这需要大量的工作,但这比从头开始要好。)