公众号旧文

如何在 Linux 服务器上按端口统计流量?

作者:胖子阅读 —

服务器商家后台显示机器用了很多流量,

但不知道这些流量是哪个服务、哪个端口产生的。

一台服务器上可能同时跑着:

  • SSH:22 端口
  • Nginx:80 / 443 端口
  • DNS:53 端口
  • Tailscale:UDP 随机端口
  • 其他自定义服务端口

如果只看总流量,很难判断到底是谁消耗了流量。

这个时候就可以用 iptables / ip6tables 做按端口流量统计。

一、基本思路

Linux 的 iptables 规则本身就带有计数器。

每一条规则都会记录:

pkts   packets 数量
bytes  字节数

所以我们可以创建一组专门用于统计的规则。

让流量经过这些规则,但不拦截、不放行、不改变原有防火墙逻辑。

只用它们的计数器来统计流量。

核心思路是:

指定端口流量 -> 单独统计
未知端口流量 -> 归到 OTHER
只统计外网网卡 -> 排除 127.0.0.1 本地回环流量
IPv4 用 iptables
IPv6 用 ip6tables

二、先确认外网网卡

先查看服务器默认出口网卡:

ip route show default

示例输出:

default via 10.57.68.1 dev eth0 proto dhcp src 10.57.68.55

其中:

dev eth0

说明外网网卡是 eth0。

后面的规则都只统计 eth0,这样可以避免把 127.0.0.1、本机端口转发、本地 DNS 等回环流量误算进去。

三、查看当前机器有哪些端口在监听

在配置统计前,建议先查看当前服务器有哪些服务正在监听端口:

ss -tulnp

这个命令可以看到:

TCP / UDP
监听地址
监听端口
对应进程

比如:

tcp LISTEN 0 128 0.0.0.0:22 users:(("sshd",pid=150,fd=3))
tcp LISTEN 0 128 127.0.0.1:8080 users:(("ssh",pid=1918,fd=5))
udp UNCONN 0 0 0.0.0.0:41641 users:(("tailscaled",pid=1166,fd=19))

这里要区分两类端口:

1. 公网监听端口

例如:

0.0.0.0:22
[::]
:22
0.0.0.0:41641

这些端口可能产生公网流量,值得统计。

2. 本机回环端口

例如:

127.0.0.1:8080
127.0.0.53:53
[::1]
:8080

这些只在本机内部使用,不应该算作公网月流量。

所以按端口统计流量时,最好配合网卡参数:

-i eth0
-o eth0

只统计外网网卡流量。

四、创建按端口统计规则

下面是一份完整示例。

假设外网网卡是 eth0。

统计的已知端口包括:

TCP:
22, 53, 80, 443, 2053, 2083, 2087, 2096, 8080, 8443, 5355
UDP:
53, 443, 8443, 36409, 41641, 5355

其中:

22       SSH
53       DNS
80/443   Web
41641    Tailscale
5355     systemd-resolve / LLMNR
36409    自定义 UDP 服务示例

可以根据自己的机器情况修改端口列表。

执行:

IFACE=eth0
TCP_PORTS="22 53 80 443 2053 2083 2087 2096 8080 8443 5355"
UDP_PORTS="53 443 8443 36409 41641 5355"
echo "当前统计网卡: $IFACE"
echo "TCP端口: $TCP_PORTS"
echo "UDP端口: $UDP_PORTS"
# 创建 IPv4 统计链
iptables -N PORT_TRAFFIC_IN 2>/dev/null
iptables -N PORT_TRAFFIC_OUT 2>/dev/null
# 创建 IPv6 统计链
ip6tables -N PORT_TRAFFIC_IN 2>/dev/null
ip6tables -N PORT_TRAFFIC_OUT 2>/dev/null
# 把统计链挂到 INPUT / OUTPUT
iptables -C INPUT -j PORT_TRAFFIC_IN 2>/dev/null || iptables -I INPUT -j PORT_TRAFFIC_IN
iptables -C OUTPUT -j PORT_TRAFFIC_OUT 2>/dev/null || iptables -I OUTPUT -j PORT_TRAFFIC_OUT
ip6tables -C INPUT -j PORT_TRAFFIC_IN 2>/dev/null || ip6tables -I INPUT -j PORT_TRAFFIC_IN
ip6tables -C OUTPUT -j PORT_TRAFFIC_OUT 2>/dev/null || ip6tables -I OUTPUT -j PORT_TRAFFIC_OUT
# 清空旧统计规则
iptables -F PORT_TRAFFIC_IN
iptables -F PORT_TRAFFIC_OUT
ip6tables -F PORT_TRAFFIC_IN
ip6tables -F PORT_TRAFFIC_OUT
# IPv4 TCP 端口统计
for port in $TCP_PORTS; do
  iptables -A PORT_TRAFFIC_IN  -i "$IFACE" -p tcp --dport "$port" -j RETURN
  iptables -A PORT_TRAFFIC_OUT -o "$IFACE" -p tcp --sport "$port" -j RETURN
done
# IPv4 UDP 端口统计
for port in $UDP_PORTS; do
  iptables -A PORT_TRAFFIC_IN  -i "$IFACE" -p udp --dport "$port" -j RETURN
  iptables -A PORT_TRAFFIC_OUT -o "$IFACE" -p udp --sport "$port" -j RETURN
done
# IPv4 OTHER:其他端口总流量
iptables -A PORT_TRAFFIC_IN  -i "$IFACE" -m comment --comment "OTHER" -j RETURN
iptables -A PORT_TRAFFIC_OUT -o "$IFACE" -m comment --comment "OTHER" -j RETURN
# IPv6 TCP 端口统计
for port in $TCP_PORTS; do
  ip6tables -A PORT_TRAFFIC_IN  -i "$IFACE" -p tcp --dport "$port" -j RETURN
  ip6tables -A PORT_TRAFFIC_OUT -o "$IFACE" -p tcp --sport "$port" -j RETURN
done
# IPv6 UDP 端口统计
for port in $UDP_PORTS; do
  ip6tables -A PORT_TRAFFIC_IN  -i "$IFACE" -p udp --dport "$port" -j RETURN
  ip6tables -A PORT_TRAFFIC_OUT -o "$IFACE" -p udp --sport "$port" -j RETURN
done
# IPv6 OTHER:其他端口总流量
ip6tables -A PORT_TRAFFIC_IN  -i "$IFACE" -m comment --comment "OTHER" -j RETURN
ip6tables -A PORT_TRAFFIC_OUT -o "$IFACE" -m comment --comment "OTHER" -j RETURN
# 清零,从当前时刻重新开始统计
iptables -Z PORT_TRAFFIC_IN
iptables -Z PORT_TRAFFIC_OUT
ip6tables -Z PORT_TRAFFIC_IN
ip6tables -Z PORT_TRAFFIC_OUT

五、为什么要加 OTHER?

如果只统计指定端口,比如只统计:

22
80
443
8443

那么如果有其他端口突然产生大量流量,比如:

3000
5000
51820
随机 UDP 端口

你是看不到的。

所以建议在最后加一条 OTHER 统计规则。

这样结果会分成两类:

已知端口:单独显示
未知端口:统一归到 OTHER

如果后面发现:

OTHER 出站 20GB

就说明有未列入统计的端口在跑流量,需要继续排查。

六、创建查看脚本

直接查看 iptables -L 输出比较难读,可以创建一个脚本,把 bytes 自动换算成 MB 和 GB。

创建脚本:

cat > /usr/local/bin/port-traffic <<'EOF'
#!/bin/bash
show_chain() {
  local CMD="$1"
  local IPVER="$2"
  local DIR="$3"
  local CHAIN="$4"
  $CMD -L "$CHAIN" -v -n -x 2>/dev/null | awk -v ipver="$IPVER" -v dir="$DIR" '
  /dpt:|spt:|OTHER/ {
    bytes=$2
    if ($0 ~ /tcp/) proto="tcp"
    else if ($0 ~ /udp/) proto="udp"
    else proto="all"
    if ($0 ~ /OTHER/) {
      port="OTHER"
    } else {
      port=$NF
      sub("dpt:","",port)
      sub("spt:","",port)
    }
    printf "%-5s %-6s %-4s %-8s %15d bytes %10.2f MB %10.4f GB\n", ipver, dir, proto, port, bytes, bytes/1024/1024, bytes/1024/1024/1024
  }'
}
echo "IP版本 方向   协议 端口              bytes         MB         GB"
echo "--------------------------------------------------------------------------"
show_chain iptables  IPv4 入站 PORT_TRAFFIC_IN
show_chain iptables  IPv4 出站 PORT_TRAFFIC_OUT
show_chain ip6tables IPv6 入站 PORT_TRAFFIC_IN
show_chain ip6tables IPv6 出站 PORT_TRAFFIC_OUT
EOF

赋权:

chmod +x /usr/local/bin/port-traffic

以后直接执行:

port-traffic

示例输出:

IP版本 方向   协议 端口              bytes         MB         GB
--------------------------------------------------------------------------
IPv4  入站 tcp  22                  7440 bytes       0.01 MB     0.0000 GB
IPv4  出站 tcp  22                 13100 bytes       0.01 MB     0.0000 GB
IPv4  入站 all  OTHER               6870 bytes       0.01 MB     0.0000 GB
IPv4  出站 all  OTHER               5256 bytes       0.01 MB     0.0000 GB
IPv6  入站 all  OTHER               1328 bytes       0.00 MB     0.0000 GB
IPv6  出站 all  OTHER               1216 bytes       0.00 MB     0.0000 GB

七、如何理解统计结果?

1. 入站和出站都要看

服务商一般统计的是总流量,通常要看:

入站 + 出站

比如:

IPv4 入站 tcp 443:10GB
IPv4 出站 tcp 443:90GB

那么 443 端口大概用了:

100GB

2. SSH 有少量流量是正常的

只要你正在登录服务器,22/tcp 就会一直有少量流量:

IPv4 入站 tcp 22
IPv4 出站 tcp 22

这属于正常情况。

3. OTHER 很小不用管

如果 OTHER 只有几 KB、几 MB,一般是系统正常通信或零散流量。

如果 OTHER 很大,比如几百 MB、几 GB、几十 GB,就要排查未知端口。

八、排查 OTHER 流量

如果发现 OTHER 很大,可以先看当前监听端口:

ss -tulnp

也可以实时查看端口连接流量:

apt install -y iftop
iftop -i eth0 -P

其中:

-i eth0

表示指定外网网卡。

-P

表示显示端口。

这样可以看到当前哪些连接、哪些端口正在跑流量。

九、清零重新开始统计

如果想从当前时间重新开始统计,可以执行:

iptables -Z PORT_TRAFFIC_IN
iptables -Z PORT_TRAFFIC_OUT
ip6tables -Z PORT_TRAFFIC_IN
ip6tables -Z PORT_TRAFFIC_OUT

然后再查看:

port-traffic

十、持久化保存规则

默认情况下,iptables 规则重启后可能丢失。

Debian / Ubuntu 可以安装:

apt update
apt install -y iptables-persistent

安装过程中如果询问:

Save current IPv4 rules?
Save current IPv6 rules?

都选择:

Yes

之后手动保存:

netfilter-persistent save

看到类似输出:

run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables save

说明 IPv4 和 IPv6 规则都保存成功了。

十一、这种方式的优缺点

优点

不需要额外安装复杂监控系统
直接利用 iptables 自带计数器
可以同时统计 IPv4 和 IPv6
可以按端口区分流量
可以排除本机回环流量
可以增加 OTHER 发现未知端口流量

缺点

每台机器都要单独配置
只能统计本机流量
端口列表需要根据服务情况维护
不能直接显示历史曲线
规则重启后需要持久化保存

总结

按端口统计 VPS 流量的一种实用方案:

iptables / ip6tables 统计链
指定外网网卡
常用端口单独统计
未知端口归到 OTHER
port-traffic 脚本格式化输出
netfilter-persistent 持久化保存

这样既能知道 22、443、8443、41641 等端口分别用了多少流量,

也能通过 OTHER 发现未知端口是否在偷偷跑流量。

对于日常排查 VPS 月流量、Tailscale 流量、Web 服务流量,这种方式已经足够实用。