go-iptables功能与源码详解

1. 什么是iptables

介绍iptables之前我们先搬出他的父亲netfilter,netfilter是基于Linux 2.4.x或更新的内核,提供了一系列报文处理的能力(过滤+改包+连接跟踪),具体来讲可以包含以下几个功能:

  • 构建基于无状态和有状态的互联网防火墙,且支持部署高可用集群
  • 如果你没有很多公网ip,那么你的局域网内部可以使用nat技术共享公网访问
  • 使用nat技术进行透明代理,你的公网暴露ip转换为你的真实的内网服务ip
  • 构建复杂的qos和策略路由器
  • 进一步的数据包操作(修改),如更改ip头部的tos/dscp/ecn位。

其实说白了,netfilter就是操作系统实现了网络防火墙的能力(连接跟踪+过滤+改包),而iptables就是用户态操作内核中防火墙能力的命令行工具,位于用户空间。快问快答,为啥计算机系统需要内核态和用户态(狗头)。

既然netfilter是对报文进行处理,那么我们就应该先了解一下内核是如何进行收发包的,发生报文大致流程如下:

  • 用户态(User Space)程序 Client 向另一台主机上的 Server 发送数据,需要通过调用内核态(Kernel Space)提供给用户态的 Socket 抽象层接口发送数据;
  • Socket 抽象层接口收到用户态数据后,向下交给传输层接口(TCP 或 UDP);
  • 传输层负责创建sk_buff,并将用户数据(应用层数据)填充到缓冲区,做合法性检查后,添加传输层头部,并通过网络层注册的接口将数据包交给网络层处理;
  • 网络层收到传输层数据包后,会查询路由表,决定数据包去向,如果是需要发出的数据包,会填充网络层头部,并交到内核虚拟网络接口设备的发送队列中;
  • 虚拟网络接口从发送队列获取数据,调用对应网卡驱动发送数据

netfilter框架就是作用于网络层中,在一些关键的报文收发处理路径上,加一些hook点,可以认为是一个个检查点,有的在主机外报文进入的位置(PREROUTING​),有的在经过路由发觉要进入本机用户态处理之前(INPUT​),有的在用户态处理完成后发出的地方(OUTPUT​),有的在报文经过路由并且发觉不是本机决定转发走的位置(FOWARD​),有的在路由转发之后出口的位置(POSTROUTING​),每个检查点有不同的规则集合,这些规则会有一定的优先级顺序,如果报文达到匹配条件(五元组之类的)且优先级最高的规则(序号越小优先级越高),内核会执行规则对应的动作,比如说拒绝,放行,记录日志,丢弃。

lua 复制代码
                                  userspace process
                                  ^                |
                                  |                |
                             _____|____       ____/___
                            /          \     /         \
                            |   input   |    |  output  |
                            __________/     _________/
                                 ^                 |
                                 |                 |
       __________            ---------       _____/_____
      /          \           |Routing |     /            \
  ---> prerouting ---------> |decision|     | postrouting |
      __________/           ----------     ____________/  
                                 |                 ^        
                            ____/___              |        
                           /         \             |        
                           | forward |--------------     
                           _________/         

最后总结如下图所示,里面包含了netfilter框架中,报文在网络层先后经过的一些hook点:

报文转发视角:

iptables命令行工具管理视角:

规则种类:

makefile 复制代码
filter表: 负责过滤功能

nat表: 网络地址转换功能

mangle表: 报文解封装,改包

raw表: 连接追踪,记录每一条连接的状态,在提升设备转发效率上起到了很大的作用

流入本机路径:

rust 复制代码
数据进入(通过网线) --> 链接网卡设备 --> 网络接口层 --> Netfilter 
--> 在网络层时会经过PREROUTING链 --> TCP UDP协议 --> 进入用户层之前(INPUT链) --> 到达用户层

经过本机路径:

rust 复制代码
数据进入 --> PREROUTING --> FORWARD --> POSTROUTING --> 出去

流出本机路径:

rust 复制代码
用户操作命令工具(iptables) --> OUTPUT链 --> ip_tables内核模块 
--> Netfilter(防火墙) --> 网络层 --> 网络接口层 --> POSTROUTING链 --> 设备驱动 --> 网络传输出

2. iptables命令实战

由上一章节我们已经知道了iptables是用户态的命令行工具,目的就是为了方便我们在各个检查点增删改查不同种类的规则,命令的格式大致如下,简单理解就是针对具体的哪些流(五元组+某些特定协议还会有更细分的匹配条件,比如说只针对tcp syn报文)进行怎样的动作(端口ip转换或者阻拦放行):

css 复制代码
iptables -t nat -I PREROUTING -d 公网IP -p tcp --dport 8080 -j DNAT --to-destination 10.1.0.1:80

iptables -A INPUT -p tcp --dport 22 -j ACCEPT

iptables -A FORWARD -p TCP ! --syn -m state --state NEW -j DROP

2.1 最基本的增删改查

增删改查的命令,我们以最常用的filter规则为例,就是最基本的防火墙过滤功能,实验环境我先准备了一个centos7的docker跑起来(docker好啊,实验完了直接删掉,不伤害本机),并通过iptables配置一些命令,然后通过主机向该docker发生ping包,测试增删改查的filter规则是否生效。

1.查询

如果有规则会把他的序号显示出来,后面插入或者删除可以用

iptables -nvL -t filter --line​

可以看出filter规则可以挂载在INPUT,FORWARD,OUTPUT检查点上,并且兜底的规则都是ACCEPT,也就是没有匹配到其他规则就全部放行,这个兜底规则是可以修改的。

我们通过ifconfig查看出docker的ip,然后主机去ping一波:​

然后再去查一下,会发现17 packets, 11414 bytes ---> 对应规则匹配到的报文的个数/字节数:

  1. 新增+删除
    新增一条拒绝的报文,我们直接把docker0网关ip给禁了,这样就无法通过主机ping通docker容器了(如果有疑问,下面有解答,会涉及docker的一些小姿势):
    iptables -I INPUT -s 172.17.0.1 -j DROP (-I不指定序号的话就是头插)
    iptables -t filter -D INPUT 1

可见已经生效了,拦截了ping包,随后我删除了这条规则,又能够ping通了

  1. 修改

    通过-R可以进行规则修改,但能修改的部分比较少,只能改action,所以我的建议是先通过编号删除规则,再在原编号位置添加一条规则。

  2. 持久化

    当我们对规则进行了修改以后,如果想要修改永久生效,必须使用service iptables save保存规则,当然,如果你误操作了规则,但是并没有保存,那么使用service iptables restart命令重启iptables以后,规则会再次回到上次保存/etc/sysconfig/iptables文件时的模样。

bash 复制代码
# 配置好yum源以后安装iptables-service 
# yum install -y iptables-services 
# 停止firewalld 
# systemctl stop firewalld 
# 禁止firewalld自动启动 
# systemctl disable firewalld 
# 启动iptables 
# systemctl start iptables 
# 将iptables设置为开机自动启动,以后即可通过iptables-service控制iptables服务 
# systemctl enable iptables

再使用service iptables save命令保存iptables规则

  1. 自定义链
    我们可以创建自己的规则集,这样统一管理会非常方便,比如说,我现在要创建一系列的web服务相关的规则集,但我查询一波INPUT链一看,妈哎,200条规则,这200条规则有针对mail服务的,有针对sshd服务的,有针对私网IP的,有针对公网IP的,我这看一遍下来头都大了,所以就产生了一个非常合理的需求,就是我能不能创建自己的规则集,然后让这些检查点引用,答案是可以的:
    iptables -t filter -N MY_WEB

iptables -t filter -I INPUT -p tcp --dport 80 -j MY_WEB

这就相当于tcp目的端口80的报文会被送入到MY_WEB规则集中进行匹配了,后面有陆续新规则进行增删时,完全可以只针对MY_WEB进行维护。

还有不少命令,详见这位大佬的总结:

Linux iptables常用命令​www.cnblogs.com/ilinuxer/p/6364064.html

回过头来,讲一个关于docker的小知识点,就是容器和如何通过主机通讯的?

这就是veth-pair技术,一端连接彼此,一端连接协议栈,evth---pair 充当一个桥梁,连接各种虚拟网络设备的。

我们在容器内和主机敲一下ifconfig:

看到了吧,容器内的eth0和主机的veth41589a9就是成对出现的,然后各个主机的虚拟网卡通过docker0互联,也实现了容器间的通信,大致如下:

我们抓个包看一哈:

root@ec58b2c87238 ens33\]# tcpdump -i eth0 -vvvvvv tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 06:03:28.342113 IP (tos 0x0, ttl 64, id 51104, offset 0, flags \[DF\], proto ICMP (1), length 84) 可以看出都是通过docker0网关转发的: ![](https://file.jishuzhan.net/article/1771012647408373762/4dcf66a0fe52c82c6c025a862189a4c4.webp) ### 2.2 一些iptables常见的配置场景 1. 允许回环连接 例如,如果您运行ping localhost或ping 127.0.0.1,您的服务器将使用回环接口对自身进行ping测试。如果 您配置应用程序服务器以使用本地主机地址连接到数据库服务器,也将使用回环接口。 sudo iptables -A INPUT -i lo -j ACCEPT sudo iptables -A OUTPUT -o lo -j ACCEPT 2. 允许已建立和相关的传入连接 由于网络流量通常需要双向(传入和传出)才能正常工作,通常会创建一个防火墙规则来允许已建立和相关的传入流量,以便服务器允许由服务器自身发起的传出连接的返回流量。 sudo iptables -A INPUT -i lo -j ACCEPT sudo iptables -A OUTPUT -o lo -j ACCEPT 3. 允许内部网络访问外部网络 假设eth0是您的外部网络接口,而eth1是您的内部网络接口,以下命令将允许内部网络访问外部网络: sudo iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT 4. 丢弃无效数据包 一些网络流量数据包会被标记为无效。有时记录此类数据包可能很有用,但通常直接丢弃它们也是可以的。 sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP 5. 阻止IP地址 要阻止源自特定IP地址(例如203.0.113.51)的网络连接 sudo iptables -A INPUT -s 203.0.113.51 -j DROP sudo iptables -A INPUT -s 203.0.113.51 -j REJECT 6. 阻止对网络接口的连接 要阻止来自特定IP地址(例如203.0.113.51)到特定网络接口(例如eth0)的连接,请使用以下命令: iptables -A INPUT -i eth0 -s 203.0.113.51 -j DROP 7. 允许所有传入的SSH连接 sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT 8. 允许特定IP地址或子网的传入SSH连接 要允许来自特定IP地址或子网的传入SSH连接,请指定源地址。例如,如果您想允许整个203.0.113.0/24子网 sudo iptables -A INPUT -p tcp -s 203.0.113.0/24 --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT 9. 允许传出SSH连接 如果您的防火墙OUTPUT策略没有设置为ACCEPT,并且您希望允许传出的SSH连接(即您的服务器主动连接到另一台服务器的SSH连接) sudo iptables -A OUTPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A INPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT 10. 允许所有传入的HTTP连接 sudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT 11. 允许所有传入的HTTPS连接 sudo iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 443 -m conntrack --ctstate ESTABLISHED -j ACCEPT 12. 允许所有传入的HTTP/HTTPS连接 sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate ESTABLISHED -j ACCEPT 13. 允许特定IP地址或子网的传入MySQL连接 要允许来自特定IP地址或子网的传入MySQL连接,请指定源地址。例如,如果您想允许整个203.0.113.0/24子网进行MySQL连接 sudo iptables -A INPUT -p tcp -s 203.0.113.0/24 --dport 3306 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 3306 -m conntrack --ctstate ESTABLISHED -j ACCEPT 14. 阻止传出的SMTP邮件 如果您的服务器不应发送传出邮件,您可能希望阻止此类流量。要阻止传出的SMTP邮件(使用端口25) sudo iptables -A OUTPUT -p tcp --dport 25 -j REJECT 这将配置iptables拒绝在端口25上的所有传出流量。如果您需要拒绝其他端口号对应的服务,请将上述的25端口替换为相应的端口号。 15. 允许所有传入的SMTP连接 要允许服务器响应端口25上的SMTP连接,请运行以下命令: sudo iptables -A INPUT -p tcp --dport 25 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 25 -m conntrack --ctstate ESTABLISHED -j ACCEPT 第二个命令允许已建立的SMTP连接的传出流量,只有在OUTPUT策略未设置为ACCEPT时才需要。 16. 允许所有传入的IMAP连接 要允许服务器响应IMAP连接(端口143),请运行以下命令: sudo iptables -A INPUT -p tcp --dport 143 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 143 -m conntrack --ctstate ESTABLISHED -j ACCEPT 第二个命令允许已建立的IMAP连接的传出流量,只有在OUTPUT策略未设置为ACCEPT时才需要。 17. 允许所有传入的POP3连接 要允许服务器响应POP3连接(端口110),请运行以下命令: sudo iptables -A INPUT -p tcp --dport 110 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 110 -m conntrack --ctstate ESTABLISHED -j ACCEPT 第二个命令允许已建立的POP3连接的传出流量,只有在OUTPUT策略未设置为ACCEPT时才需要。 18. 允许所有传入的POP3S连接 要允许服务器响应POP3S连接(端口995),请运行以下命令: sudo iptables -A INPUT -p tcp --dport 995 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p tcp --sport 995 -m conntrack --ctstate ESTABLISHED -j ACCEPT 第二个命令允许已建立的POP3S连接的传出流量,只有在OUTPUT策略未设置为ACCEPT时才需要。 19. 隐藏网络内部主机的IP地址,还能够让局域网内的主机共享公网IP,让使用私网IP的主机能够访问互联网,snat iptables -t nat -A POSTROUTING -s 10.1.0.0/16 -j SNAT --to-source 公网IP 如果公网IP是动态获取的,不是固定的,则可以使用MASQUERADE进行动态的SNAT操作,如下命令表示将10.1网段的报文的源IP修改为eth0网卡中可用的地址。 iptables -t nat -A POSTROUTING -s 10.1.0.0/16 -o eth0 -j MASQUERADE 20. 透明代理,将公网ip转为实际服务的内网ip,dnat iptables -t nat -I PREROUTING -d 公网IP -p tcp --dport 公网端口 -j DNAT --to-destination 私网IP:端口号 iptables -t nat -A POSTROUTING -s 10.1.0.0/16 -j SNAT --to-source 公网IP 21. 端口映射 iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080 其他机器访问本机的80端口时,会被映射到8080端口,这也是docker端口映射的原理。 最后引用一波[朱老板](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fwww.zsythink.net%2Farchives%2F1869 "https://link.zhihu.com/?target=https%3A//www.zsythink.net/archives/1869")总结的常用套路,作为本章结尾: 1、规则的顺序非常重要。 如果报文已经被前面的规则匹配到,IPTABLES则会对报文执行对应的动作,通常是ACCEPT或者REJECT,报文被放行或拒绝以后,即使后面的规则也能匹配到刚才放行或拒绝的报文,也没有机会再对报文执行相应的动作了(前面规则的动作为LOG时除外),所以,针对相同服务的规则,更严格的规则应该放在前面。 2、当规则中有多个匹配条件时,条件之间默认存在"与"的关系。 如果一条规则中包含了多个匹配条件,那么报文必须同时满足这个规则中的所有匹配条件,报文才能被这条规则匹配到。 3、在不考虑1的情况下,应该将更容易被匹配到的规则放置在前面。 4、当IPTABLES所在主机作为网络防火墙时,在配置规则时,应着重考虑方向性,双向都要考虑,从外到内,从内到外。 5、在配置IPTABLES白名单时,往往会将链的默认策略设置为ACCEPT,通过在链的最后设置REJECT规则实现白名单机制,而不是将链的默认策略设置为DROP,如果将链的默认策略设置为DROP,当链中的规则被清空时,管理员的请求也将会被DROP掉。 ## 3. go-iptables安装 go-iptables是组件库,直接一波import "[github.com/coreos/go-i...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttp%253A%2F%2Fgithub.com%2Fcoreos%2Fgo-iptables%2Fiptables "https://link.zhihu.com/?target=http%3A//github.com/coreos/go-iptables/iptables")"​,然后go mod tidy一番,就准备兴致冲冲的跑一波自带的测试用例集,没想到上来就是4个error: ![](https://file.jishuzhan.net/article/1771012647408373762/3b037e03c4ebdd815e0ec6e249ed2959.webp) 这还了得,我直接去go-iptables的仓库issue上瞅瞅有没有同道中人,果然发现一个类似问题: ![](https://file.jishuzhan.net/article/1771012647408373762/887a17818c96b43987f3f16e57ddf23a.webp) 虽然都是test failures,但是错的原因是不一样的,但是看他的版本是1.8的,所以我怀疑是我的iptables的版本太老了,一个iptables -v看一眼: ![](https://file.jishuzhan.net/article/1771012647408373762/3d6f7c6cdd43a51c2f2a51fb0e5c286d.webp) 直接用yum update好像不能升级,yum search也没看到最新版本,看来只能下载iptables源码自己编译了,一套连招先打出来: ```bash git clone git://git.netfilter.org/iptables cd libnftnl sh autogen.sh ./configure make make install ``` 不出意外的话,那就得出点意外了: ![](https://file.jishuzhan.net/article/1771012647408373762/7e781b9fcd45dc8f1b03e4a3d656164b.webp) 那就继续下载源码安装吧,然后发现libmnl​又依赖libnftnl​,所以直接一波大招,netfilter全家桶全安装: ```bash yum install autoconf autogen nftables.x86_64 -y git clone git://git.netfilter.org/libmnl cd libmnl sh autogen.sh ./configure make make install git clone git://git.netfilter.org/libnftnl cd libnftnl sh autogen.sh ./configure make make install PKG_CONFIG_PATH=/usr/local/lib/pkgconfig export PKG_CONFIG_PATH # 重新进到iptables目录 ./configure make make install ``` ![](https://file.jishuzhan.net/article/1771012647408373762/a82269001d68fe249168540b91134dd2.webp) Finally,再跑一次测试用例就成功了,下面就可以愉快的阅读源码了: ![](https://file.jishuzhan.net/article/1771012647408373762/48c5c5bb136ef22351cfab890cde65a5.webp) ## 4. 如何使用go-iptables ```go // 新建ipv4 iptables // 默认是ipv4,等待iptables lock的超时时间为永远,无限等待 ipt, err := New() if err != nil { panic(fmt.Sprintf("New failed: %v", err)) } // 新建ipv6 iptables ip6t, err := NewWithProtocol(ProtocolIPv6) if err != nil { panic(fmt.Sprintf("NewWithProtocol(ProtocolIPv6) failed: %v", err)) } // 列出filter表的所有链 // output: [INPUT FORWARD OUTPUT DOCKER DOCKER-ISOLATION FORWARD FORWARD_IN_ZONES_SOURCE OUTPUT_direct] originalListChain, err := ipt.ListChains("filter") // 清除filter表内的某个链的规则,如果没有这个链的话会创建一个出来 chain := "TEST-91382" err = ipt.ClearChain("filter", chain) // filter表是否存在"TEST-91382"这个链 // output: true exists, err := ipt.ChainExists("filter", chain) // 删除filter表内的某个链,但不能删除非空的链,否则会报err ---> e.IsNotExist() err = ipt.DeleteChain("filter", chain) // filter表,"TEST-91382"链尾部追加一条规则 err = ipt.Append("filter", chain, "-s", "0/0", "-j", "ACCEPT") // 自定义链重命名 newChain := "TEST-91383" err = ipt.RenameChain("filter", chain, newChain) // 清空链的规则,并删除这个链 err = ipt.ClearAndDeleteChain("filter", chain) address1 = "203.0.113.1/32" address2 = "203.0.113.2/32" subnet1 = "192.0.2.0/24" subnet2 = "198.51.100.0/24" // address1 = "2001:db8::1/128" // address2 = "2001:db8::2/128" // subnet1 = "2001:db8:a::/48" // subnet2 = "2001:db8:b::/48" // 尾部追加,但如果链中已经存在一样的规则,那就不会重复新增了 err = ipt.AppendUnique("filter", chain, "-s", subnet1, "-d", address1, "-j", "ACCEPT") // 指定位置新增规则 err = ipt.Insert("filter", chain, 2, "-s", subnet2, "-d", address2, "-j", "ACCEPT") // 指定位置新增规则, 但如果链中已经存在一样的规则,那就不会重复新增了 err = ipt.InsertUnique("filter", chain, 2, "-s", subnet2, "-d", address2, "-j", "ACCEPT") // 删除规则 err = ipt.Delete("filter", chain, "-s", subnet1, "-d", address2, "-j", "ACCEPT") // 修改规则 err = ipt.Replace("filter", chain, 1, "-s", subnet2, "-d", address2, "-j", "ACCEPT") // 展示链中的所有具体规则 rules, err := ipt.List("filter", chain) // 展示链中某一位置的规则 rule, err := ipt.ListById("nat", "PREROUTING", 1) // 删除规则,如果存在的话 err = ipt.DeleteIfExists("filter", chain, "-s", address1, "-d", subnet2, "-j", "ACCEPT") // 一些error信息 var isNotExistPatterns = []string{ "Bad rule (does a matching rule exist in that chain?).\n", "No chain/target/match by that name.\n", "No such file or directory", "does not exist", } ``` ## 5. go-iptables源码分析 关键结构体IPTables ```go type IPTables struct { path string // iptables可执行路径 proto Protocol // ipv4还是v6 hasCheck bool // 是否支持--check命令 hasWait bool // 是否支持--wait命令 waitSupportSecond bool // 是否支持--wait second 命令 hasRandomFully bool v1 int // 版本号1.8.1 -> v1=1, v2=8, v3=1 v2 int v3 int mode string // the underlying iptables operating mode, e.g. nf_tables timeout int // time to wait for the iptables lock, default waits forever } ``` 初始化函数func New(opts ...option) (\*IPTables, error)​,流程如下: ![](https://file.jishuzhan.net/article/1771012647408373762/79370860604ef87cb0b3526a9770b1c8.webp) ```go 可以认为是一个建造者模式,灰常优雅 type option func(*IPTables) func IPFamily(proto Protocol) option { return func(ipt *IPTables) { ipt.proto = proto } } func Timeout(timeout int) option { return func(ipt *IPTables) { ipt.timeout = timeout } } func Path(path string) option { return func(ipt *IPTables) { ipt.path = path } } func New(opts ...option) (*IPTables, error) -> for _, opt := range opts { opt(ipt) } // example ipt2, err := New(Timeout(5)) ipt2, err := New(IPFamily(proto), Timeout(0)) ``` 几个重要函数的实现: 1. ​func (ipt \*IPTables) ListChains(table string) (\[\]string, error)​ 通过执行命令行 iptables -t table名 -S​ 返回的字符串通过词法解析即可: Format is the following: -P OUTPUT ACCEPT -N Custom 2. ​func (ipt \*IPTables) ClearChain(table, chain string) error​ 命令行执行:先创建再flush ipt.run("-t", table, "-N", chain)​+ ipt.run("-t", table, "-F", chain)​ 3. ​Stats​统计rules including the byte and packet counts​ 执行命令行:"-t", table, "-L", chain, "-n", "-v", "-x"​ 分词解析:0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options​ 其他好像也米有什么,这里面就主要介绍一下,他的命令行执行是怎么实现的: ```go func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { args = append([]string{ipt.path}, args...) if ipt.hasWait { // 1.6.0以后的版本支持--wait命令,可以获取iptables锁 args = append(args, "--wait") if ipt.timeout != 0 && ipt.waitSupportSecond { args = append(args, strconv.Itoa(ipt.timeout)) } } else { // 不支持我就自己创建一个xtables文件锁 fmu, err := newXtablesFileLock() if err != nil { return err } // tryLock尝试在不阻塞的情况下对xtables锁文件进行独占锁定,拿不到会报err -> resource temporarily unavailable // // best-effort机制,flock只是用于检测文件是否被加锁,针对文件已经被加锁, // 另一个进程写入数据的情况,内核不会阻止这个进程的写入操作 // // flock锁的释放非常具有特色,即可调用LOCK_UN参数来释放文件锁, // 也可以通过关闭fd的方式来释放文件锁(flock的第一个参数是fd),意味着flock会随着进程的关闭而被自动释放掉。 // ps: flock其中的一个使用场景为:检测进程是否已经存在。 ul, err := fmu.tryLock() if err != nil { syscall.Close(fmu.fd) return err } defer func() { _ = ul.Unlock() }() } var stderr bytes.Buffer cmd := exec.Cmd{ Path: ipt.path, Args: args, Stdout: stdout, Stderr: &stderr, } // 关键调用cmd.Run() if err := cmd.Run(); err != nil { switch e := err.(type) { case *exec.ExitError: return &Error{*e, cmd, stderr.String(), nil} default: return err } } return nil } ``` ## 6. Reference * [dev.to/isabelcmdco...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fdev.to%2Fisabelcmdcosta%2Finstalling-nftables-from-sources-ondebian--4ic "https://link.zhihu.com/?target=https%3A//dev.to/isabelcmdcosta/installing-nftables-from-sources-ondebian--4ic") * [colobu.com/2023/10/30/...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fcolobu.com%2F2023%2F10%2F30%2Fiptables-and-operations-in-Go%2F "https://link.zhihu.com/?target=https%3A//colobu.com/2023/10/30/iptables-and-operations-in-Go/") * [zhuanlan.zhihu.com/p/618848653](https://link.juejin.cn?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F618848653 "https://zhuanlan.zhihu.com/p/618848653") * [blog.csdn.net/weixin_4214...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fblog.csdn.net%2Fweixin_42141701%2Farticle%2Fdetails%2F106866163 "https://link.zhihu.com/?target=https%3A//blog.csdn.net/weixin_42141701/article/details/106866163") * [www.cnblogs.com/marility/p/...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fwww.cnblogs.com%2Fmarility%2Fp%2F7448407.html "https://link.zhihu.com/?target=https%3A//www.cnblogs.com/marility/p/7448407.html") * [www.zsythink.net/archives/17...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fwww.zsythink.net%2Farchives%2F1764 "https://link.zhihu.com/?target=https%3A//www.zsythink.net/archives/1764") * [blog.csdn.net/u013538795/...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fblog.csdn.net%2Fu013538795%2Farticle%2Fdetails%2F105702364 "https://link.zhihu.com/?target=https%3A//blog.csdn.net/u013538795/article/details/105702364") * [www.cnblogs.com/bakari/p/10...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fwww.cnblogs.com%2Fbakari%2Fp%2F10613710.html "https://link.zhihu.com/?target=https%3A//www.cnblogs.com/bakari/p/10613710.html") * [www.zhihu.com/question/36...](https://link.juejin.cn?target=https%3A%2F%2Fwww.zhihu.com%2Fquestion%2F369780528%2Fanswer%2F2491949714 "https://www.zhihu.com/question/369780528/answer/2491949714") * [netfilter.org/](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fnetfilter.org%2F "https://link.zhihu.com/?target=https%3A//netfilter.org/") \& [netfilter.org/projects/ip...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fnetfilter.org%2Fprojects%2Fiptables%2Findex.html "https://link.zhihu.com/?target=https%3A//netfilter.org/projects/iptables/index.html") * [zhuanlan.zhihu.com/p/93630586](https://link.juejin.cn?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F93630586 "https://zhuanlan.zhihu.com/p/93630586") * [www.cnblogs.com/semwu/p/157...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fwww.cnblogs.com%2Fsemwu%2Fp%2F15734741.html "https://link.zhihu.com/?target=https%3A//www.cnblogs.com/semwu/p/15734741.html") * [zhuanlan.zhihu.com/p/395752839](https://link.juejin.cn?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F395752839 "https://zhuanlan.zhihu.com/p/395752839") * [www.digitalocean.com/community/t...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fwww.digitalocean.com%2Fcommunity%2Ftutorials%2Fiptables-essentials-common-firewall-rules-and-commands "https://link.zhihu.com/?target=https%3A//www.digitalocean.com/community/tutorials/iptables-essentials-common-firewall-rules-and-commands") * [blog.csdn.net/sinat_38816...](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fblog.csdn.net%2Fsinat_38816924%2Farticle%2Fdetails%2F129001218 "https://link.zhihu.com/?target=https%3A//blog.csdn.net/sinat_38816924/article/details/129001218") * [zhuanlan.zhihu.com/p/25134841](https://link.juejin.cn?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F25134841 "https://zhuanlan.zhihu.com/p/25134841") * [blog.csdn.net/asmartkil](https://link.juejin.cn?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%253A%2F%2Fblog.csdn.net%2Fasmartkiller%2Farticle%2Fdetails%2F120983546 "https://link.zhihu.com/?target=https%3A//blog.csdn.net/asmartkiller/article/details/120983546")

相关推荐
跟着珅聪学java1 小时前
spring boot +Elment UI 上传文件教程
java·spring boot·后端·ui·elementui·vue
徐小黑ACG2 小时前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
战族狼魂5 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
杉之6 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch7 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
bobz9657 小时前
k8s 怎么提供虚拟机更好
后端
bobz9658 小时前
nova compute 如何创建 ovs 端口
后端
用键盘当武器的秋刀鱼8 小时前
springBoot统一响应类型3.5.1版本
java·spring boot·后端
Asthenia04129 小时前
从迷宫到公式:为 NFA 构造正规式
后端
Asthenia04129 小时前
像整理玩具一样:DFA 化简和状态等价性
后端