利用nftables做端口流量统计
Reverse Lv4

概述

通过nftables规则对特定端口范围做计数流量统计,可以借此计算瞬时流量.

写规则

假设这里我统计的是11000-19000端口范围的流量,入站和出站都统计,然后用计数器来计算流量.

大概流程就是创一个名字叫portstats的表,然后创两个链,一个叫input_chain,一个叫output_chain,

然后创两个计数器,一个叫input_tcp_range,一个叫output_tcp_range,然后创两个规则,一个叫input_tcp_range,一个叫output_tcp_range.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 创建表和链
nft add table inet portstats
nft add chain inet portstats input_chain { type filter hook input priority 0\; }
nft add chain inet portstats output_chain { type filter hook output priority 0\; }

# 创建表
nft add table inet portstats

# 创建 input 链(接收进入本机的数据)
nft add chain inet portstats input_chain { type filter hook input priority 0\; }

# 创建 output 链(从本机发出的数据)
nft add chain inet portstats output_chain { type filter hook output priority 0\; }

# 创建计数器 TCP
nft add counter inet portstats input_tcp_range
nft add counter inet portstats output_tcp_range

# 创建计数器 UDP
nft add counter inet portstats input_udp_range
nft add counter inet portstats output_udp_range

# 入站 TCP
nft add rule inet portstats input_chain tcp dport 11000-19000 counter name input_tcp_range
# 入站 UDP
nft add rule inet portstats input_chain udp dport 11000-19000 counter name input_udp_range

# 出站 TCP
nft add rule inet portstats output_chain tcp sport 11000-19000 counter name output_tcp_range
# 出站 UDP
nft add rule inet portstats output_chain udp sport 11000-19000 counter name output_udp_range

创建完成后,可以查看这个表,能直接看到他的流量统计.

在输出中,能看到counter的流量统计,packets是包的数量,bytes是字节数.

  • input_tcp_range 入站 TCP 流量统计
  • output_tcp_range 出站 TCP 流量统计
  • input_udp_range 入站 UDP 流量统计
  • output_udp_range 出站 UDP 流量统计

当然,你可以直接通过他的 bytes 来计算计数流量,包括瞬时流量你也能记录到.

1
nft list table inet portstats
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
root@localhost:~# nft list table inet portstats
table inet portstats {
counter input_tcp_range {
packets 3646743 bytes 234844099
}

counter output_tcp_range {
packets 2718337 bytes 7699060368
}

counter input_udp_range {
packets 83 bytes 12353
}

counter output_udp_range {
packets 96 bytes 7375
}

chain input_chain {
type filter hook input priority filter; policy accept;
tcp dport 11000-19000 counter name "input_tcp_range"
udp dport 11000-19000 counter name "input_udp_range"
tcp dport 11000-19000 counter name "input_tcp_range"
udp dport 11000-19000 counter name "input_udp_range"
}

chain output_chain {
type filter hook output priority filter; policy accept;
tcp sport 11000-19000 counter name "output_tcp_range"
udp sport 11000-19000 counter name "output_udp_range"
tcp sport 11000-19000 counter name "output_tcp_range"
udp sport 11000-19000 counter name "output_udp_range"
}
}
root@localhost:~#

流量计算

比如上面的output_tcp_range当前流量在7699060368字节,如果要转换到 Mb 单位,你应该:

1
echo 'scale=2; 7699060368 / 1048576' | bc

单位是 Mb,这样计算出来的结果仅仅只是计数器记录的流量,并不是瞬时流量.

1
7342.39

计算瞬时流量

原理也很简单,先获取当前计数器记录的流量,然后 sleep10 秒后,再获取一次计数器记录的流量,然后计算两次的差值.

1
2
3
nft list table inet portstats
sleep 10 # 等待 10 秒再取一次
nft list table inet portstats

比如我这里两次执行结果的input_tcp_range.

  • 第一次 234844099
  • 第二次 4346338221
  • 那么瞬时流量计算方式直接参考以下脚本

第二次的结果减去第一次的结果,并且除以 10 的延迟秒,得到瞬时流量(如果要到 Mb 单位则再除以 1048576).

如果你的延迟设置的不是 10 秒,而是 60 秒,那你就得除以 60.

1
echo 'scale=2; (4346338221 - 234844099) / 1048576 / 10' | bc # 单位是 Mb/s
1
392.10

删除规则

如果后续你不想要这个表或者这些规则了,可以直接删除这张表,他会将这个表下的规则,链,计数器一并清理.

1
nft delete table inet portstats