几年前,我曾在一家小型互联网服务提供商担任 Linux 系统管理员。除了常规的系统管理职责外,我还有幸为 Linux 编写有用的软件和工具。我的任务之一就是编写一个记录每位客户使用流量情况的实用程序。
这家 ISP 的网络结构非常简单。互联网网关是一台功能强大的多核 Linux 服务器,它充当路由器、防火墙,并进行流量整形。现在,它还需要执行流量统计工作。

那时,我已经掌握了 iptables,并且注意到在列出已有规则时,iptables 会显示每个规则匹配的数据包数量和总字节数。我想,为什么不利用这个功能来做流量统计呢?于是我就这么做了。我编写了一个脚本,为每个用户的 IP 地址创建一个始终接受流量并让其通过防火墙的空规则,以及另一个脚本来提取数据包和字节数。
以下是我具体实现方法的详细说明。
首先,让我们看看刚启动系统时 iptables 显示的信息。
# iptables -L -n -v -xChain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination刚启动后,系统未添加任何规则,也没有流量通过。让我们熟悉一下当存在规则时,我们将关注的那些输出信息。注意到 pkts 和 bytes 列了吗?'pkts' 代表数据包,显示规则匹配的数据包总数。'bytes' 代表规则匹配的字节总数。同时请注意三个所谓的“链”—— INPUT、FORWARD 和 OUTPUT。INPUT 链处理目标地址为 Linux 主机本身的数据包,OUTPUT 链处理离开 Linux 主机(由运行在 Linux 主机上的程序生成)的数据包,而 FORWARD 链处理通过主机的数据包。
你可能也对我使用的命令行参数感兴趣:
生产环境中的防火墙通常已经在 FORWARD 链中配置了各种规则。为了避免干扰它们,我们创建一个名为 TRAFFIC_ACCT 的新流量统计链:
#iptables -N TRAFFIC_ACCT# iptables -L -n -v -xChain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain TRAFFIC_ACCT (0 references) pkts bytes target prot opt in out source destination现在,我们将把所有经过该主机的流量重定向到 TRAFFIC_ACCT 链中的规则进行匹配:
#iptables -I FORWARD -j TRAFFIC_ACCT# iptables -L -n -v -xChain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 TRAFFIC_ACCT all -- * * 0.0.0.0/0 0.0.0.0/0Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain TRAFFIC_ACCT (0 references) pkts bytes target prot opt in out source destination如果你使用的是 Linux 桌面电脑,那么可以在 INPUT 链中插入相同的规则(iptables -I INPUT -j TRAFFIC_ACCT),因为数据包不会被路由转发,而是直接发送到你的电脑。
命令行参数 -L 实际上可以指定链的名称来列出其规则。从现在起,我们将只关注 TRAFFIC_ACCT 链的规则:
# iptables -L -n -v -xChain TRAFFIC_ACCT (1 references) pkts bytes target prot opt in out source destination为了说明主要思路,我们可以对规则进行一些操作。例如,我们来按 tcp、udp 和 icmp 协议划分流量统计。为此,我们在 TRAFFIC_ACCT 链中插入三条规则——一条匹配 tcp 协议,一条匹配 udp 协议,最后一条匹配 icmp 协议。
# iptables -A TRAFFIC_ACCT -p tcp# iptables -A TRAFFIC_ACCT -p udp# iptables -A TRAFFIC_ACCT -p icmp一段时间后,让我们看看统计结果:
# iptables -L TRAFFIC_ACCT -n -v -xChain TRAFFIC_ACCT (1 references) pkts bytes target prot opt in out source destination 4356 2151124 tcp -- * * 0.0.0.0/0 0.0.0.0/0 119 15964 udp -- * * 0.0.0.0/0 0.0.0.0/0 3 168 icmp -- * * 0.0.0.0/0 0.0.0.0/0我们可以看到,共有 4356 个 TCP 数据包(总计 2151124 字节,即 2 兆字节)通过了防火墙,119 个 UDP 数据包和 3 个 ICMP 数据包。
您可以通过 -Z iptables 命令将计数器清零:
# iptables -Z TRAFFIC_ACCT# iptables -L TRAFFIC_ACCT -n -v -xChain TRAFFIC_ACCT (1 references) pkts bytes target prot opt in out source destination 0 0 tcp -- * * 0.0.0.0/0 0.0.0.0/0 0 0 udp -- * * 0.0.0.0/0 0.0.0.0/0 0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0您可以使用 -F iptables 命令清空 TRAFFIC_ACCT 链中的所有规则:
# iptables -F TRAFFIC_ACCT# iptables -L TRAFFIC_ACCT -n -v -xChain TRAFFIC_ACCT (1 references) pkts bytes target prot opt in out source destination另一个有趣的示例是统计已建立的连接数量:
# iptables -A TRAFFIC_ACCT -p tcp --syn# iptables -L -n -v -xChain TRAFFIC_ACCT (1 references) pkts bytes target prot opt in out source destination 5 276 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp flags:0x16/0x02结果显示已发送 5 个发起连接的 TCP 数据包。非常简洁,不是吗?
在我的实际应用中,我向 TRAFFIC_ACCT 链中添加了用户 IP 地址。然后定期列出和记录流量数据并清零计数器。
您甚至可以创建两个链 TRAFFIC_ACCT_IN 和 TRAFFIC_ACCT_OUT 来分别匹配入站和出站流量:
# iptables -N TRAFFIC_ACCT_IN# iptables -N TRAFFIC_ACCT_OUT# iptables -I FORWARD -i eth0 -j TRAFFIC_ACCT_IN# iptables -I FORWARD -o eth0 -j TRAFFIC_ACCT_OUT# iptables -L -n -v -xChain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination 0 0 TRAFFIC_ACCT_OUT all -- * eth0 0.0.0.0/0 0.0.0.0/0 0 0 TRAFFIC_ACCT_IN all -- eth0 * 0.0.0.0/0 0.0.0.0/0Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destinationChain TRAFFIC_ACCT_IN (1 references) pkts bytes target prot opt in out source destinationChain TRAFFIC_ACCT_OUT (1 references) pkts bytes target prot opt in out source destination例如,要记录 IP 地址 192.168.1.2 和 192.168.1.3 的入站和出站流量使用情况,可以执行:
# iptables -A TRAFFIC_ACCT_IN --dst 192.168.1.2# iptables -A TRAFFIC_ACCT_IN --dst 192.168.1.3# iptables -A TRAFFIC_ACCT_OUT --src 192.168.1.2# iptables -A TRAFFIC_ACCT_OUT --src 192.168.1.3查看规则列表:
# iptables -L TRAFFIC_ACCT_IN -n -v -xChain TRAFFIC_ACCT_IN (1 references) pkts bytes target prot opt in out source destination 368 362120 all -- * * 0.0.0.0/0 192.168.1.2 61 9186 all -- * * 0.0.0.0/0 192.168.1.3# iptables -L TRAFFIC_ACCT_OUT -n -v -xChain TRAFFIC_ACCT_OUT (1 references) pkts bytes target prot opt in out source destination 373 22687 all -- * * 192.168.1.2 0.0.0.0/0 101 44711 all -- * * 192.168.1.3 0.0.0.0/0至此演示结束。可以看到,在 Linux 机器上实现精确流量统计非常简单。您甚至可以对流量进行可视化,并创建绘制精美图表的 Web 应用程序。
通过结合使用 iptables 和 awk 命令,可以优美地输出数据使用量:
# iptables -L TRAFFIC_ACCT_IN -n -v -x | awk '$1 ~ /^[0-9]+$/ { printf "IP: %s, %d bytes\n", $8, $2 }'IP: 192.168.1.2, 1437631 bytesIP: 192.168.1.3, 449554 bytes# iptables -L TRAFFIC_ACCT_OUT -n -v -x | awk '$1 ~ /^[0-9]+$/ { printf "IP: %s, %d bytes\n", $7, $2 }'IP: 192.168.1.2, 88202 bytesIP: 192.168.1.3, 244848 bytes我最初是从这个教程学习 IPTables 的。这是该主题下能找到的最佳教程。
下次我将介绍如何结合使用 tc 和 iptables 实现流量整形。到时见!
https://catonmat.net/traffic-accounting-with-iptables