我使用“tcpdump”捕获流量,并希望按 HTTP 方法进行过滤。当我有 IPv4 数据包时,我使用:tcpdump -s 0 -A 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'
过滤 HTTP GET 数据包。
不幸的是,该tcp[]
过滤器不适用于 IPv6 数据包。根据文档
针对传输层报头的算术表达式(例如
tcp[0]
)对 IPv6 数据包不起作用。
因此,我正在寻找另一种方法,使用 tcpdump 仅过滤 IPv6 流量的 HTTP 方法。
我曾尝试寻找解决方案,但似乎它们包含的 grep 用法不适合我的需要,因为我想将所有过滤后的流量直接输出到 pcap 文件
答案1
tcpdump
自 4.99.x 版本起 IPv6的限制
对于 IPv4,要获取数据包中的有效载荷偏移量,可以使用以下方法或多或少地计算有效载荷偏移量IPv4 的国际人道法+ TCP 的数据偏移量还考虑 IPv4 选项和 TCP 选项。这很简单,可以包含在tcpdump直接在生成 BPF 字节码过滤器时(例如,一些 BPF 文档可用那里)。
相反,对于 IPv6,在固定报头和上报头之间可以有可变(可能在 0 到 9 之间)数量的扩展报头。这意味着可能包含 10 种情况(0、1、2、... 9 个扩展报头)的代码来查找数据包中的有效负载偏移量,假设某些情况没有其他特殊性,甚至没有尝试针对格式错误的数据包(这可能导致误报匹配)进行保护:我只能假设这没有实现,因为复杂性和不愿意提供忽略此类报头的有缺陷的实现。
即使是一个简单的测试,例如ip6 and tcp dst port 80
不考虑扩展头,如下面的生成的 BPF 代码所示:
$ tcpdump --version
tcpdump version 4.99.3
libpcap version 1.10.3 (with TPACKET_V3)
OpenSSL 3.0.9 30 May 2023
$ tcpdump -y EN10MB -d ip6 and tcp dst port 80
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 7
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 7
(004) ldh [56]
(005) jeq #0x50 jt 6 jf 7
(006) ret #262144
(007) ret #0
所以可能会错过交通。
从 tcpdump 4.99.x 开始,唯一可以充分处理 IPv6 扩展头的地方似乎记录在手册页的末尾:
ip6 proto
应该追逐头链,但目前还没有。ip6 protochain
为此行为提供了。
事实上,目前 tcpdump 4.99.xip6 and tcp
相当于ip6 proto 6
(它们产生相同的字节码):
$ tcpdump -y EN10MB -d ip6 and tcp
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 8
(002) ldb [20]
(003) jeq #0x6 jt 7 jf 4
(004) jeq #0x2c jt 5 jf 8
(005) ldb [54]
(006) jeq #0x6 jt 7 jf 8
(007) ret #262144
(008) ret #0
whileip6 protochain 6
进行了更彻底的检查,并且是在任何涉及上层(最终传输头)的测试中应该进行的,例如 TCP:
$ tcpdump -y EN10MB -d ip6 protochain 6
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 35
(002) ldb [20]
(003) ldx #0x28
(004) jeq #0x6 jt 32 jf 5
(005) jeq #0x3b jt 32 jf 6
(006) jeq #0x0 jt 10 jf 7
(007) jeq #0x3c jt 10 jf 8
(008) jeq #0x2b jt 10 jf 9
(009) jeq #0x2c jt 10 jf 19
(010) ldb [x + 14]
(011) st M[0]
(012) ldb [x + 15]
(013) add #1
(014) mul #8
(015) add x
(016) tax
(017) ld M[0]
(018) ja 4
(019) jeq #0x33 jt 20 jf 32
(020) txa
(021) ldb [x + 14]
(022) st M[0]
(023) txa
(024) add #1
(025) tax
(026) ldb [x + 14]
(027) add #2
(028) mul #4
(029) tax
(030) ld M[0]
(031) ja 4
(032) add #0
(033) jeq #0x6 jt 34 jf 35
(034) ret #262144
(035) ret #0
上面的 4 个测试(包括jt 10
和之间的行(10
)并(018) ja 4
循环遍历 4 个特定的 IPv6 扩展标头以跳过它们,同时(19)
处理(31)
IPSec AH。我不知道如何在其他地方生成这样的代码tcpdump语言(而不是直接带通滤波器代码)。使用这种方法ip6 protochain 6 and ip6 and tcp dst port 80
只会从头开始,甚至更糟,而不是利用刚刚获得的 TCP 标头。
Linux 的各种网络设施和工具中都描述了同一类问题。例如:tc u32
告诉:
icmp_code
值掩码 8假设下一个标头协议为 icmp 或 ipv6-icmp,并匹配类型或代码字段值。这是很危险的,因为代码假设IPv4 的最小报头大小和缺少 IPv6 扩展标头。
所有这些甚至没有考虑到对于 HTTP 而言,当重新使用连接时,下一个 HTTP 查询甚至可能不在数据包边界的开始处,或者如果此类数据出现在数据包边界的开始处,则嵌入为数据而不是查询的查询可能会匹配。
tcpdump
使用版本 4.99.x测试 0 扩展头的情况
这是使用 0 个扩展头进行的测试,即与tcpdump目前确实如此,只需进行最低限度的验证:
tcpdump -n -s 0 -A 'ip6[6] == 6 and ( ip6[4:2] - ((ip6[52] & 0xf0) >> 2) >= 4 ) and ip6[40 + ((ip6[52] & 0xf0) >> 2) :4] == 0x47455420'
可以像这样记录(仍然只针对 0 扩展头的情况,其中 TCP 头始终从位置 40 开始):
下一个标头是 TCP
ip6[6] == 6
IPv6有效载荷长度:
ip6[4:2]
TCP数据偏移量(固定 IPv6 的大小为 40 + 12 = 52)(并进行适当调整以获取字节)
(ip6[52] & 0xf0) >> 2
TCP 有效载荷长度 = IPv6 有效载荷长度 - TCP 数据偏移量
测试 TCP 有效负载是否至少有 4 个字节,长度至少为“GET”
ip6[4:2] - ((ip6[52] & 0xf0) >> 2) >= 4
以 ASCII/UTF-8 编码的 4 字节字符串“GET”可以用 4 字节值 0x47455420 表示
TCP 有效载荷偏移量为 IPv6 固定报头长度 (40) + TCP 数据偏移量
40 + ((ip6[52] & 0xf0) >> 2)
测试从 IPv6 固定报头长度 (40) + 数据偏移量开始的第一个 4 字节字是否等于字符串“GET”的值
ip6[40 + ((ip6[52] & 0xf0) >> 2) :4] = 0x47455420
nftablesLinux 内核 >= 5.15.54 +tcpdump
我建议使用一种简单的替代 Linux 专用方法nftables并且需要足够新的内核(Linux 内核版本>= 5.15.54反向移植自5.16) 也nftables>= 1.0.1支持@ih
(内部标头/有效载荷)原始有效载荷,而不仅仅是@th
(传输头),因为nftables其本身太过有限,无法以@th
有用的方式进行任意处理。
IPv6 数据报已被解析,包括任何 IPv6 扩展报头,并且 @th 和 @in 指针已经可用于nftables:无需进一步处理即可获得所有情况。我依靠nftables如果数据包太短,则检查失败,而无需计算实际的 TCP 有效负载大小(与tcpdump和 BPF,nftables无法进行减法,因此无法正确检查大小)。
这nftables下面的示例试图模仿 OP 的精确示例,但如果不进行一些调整,就无法在所有细节上保持一致。没有方向也没有端口,在 NAT 之前捕获传入数据包,在 NAT 之后捕获传出数据包(因此选择了钩子和优先级)。最终将选定的数据包发送至日志设施:
在第二种调用形式中(如果nflog_group指定后,Linux 内核会将数据包传递给 nfnetlink_log,后者会通过 netlink 套接字将日志发送到指定组。一个用户空间进程可以订阅该组以接收日志 [...]
table ip6 special_log
delete table ip6 special_log
table ip6 special_log {
chain log_to_nflog {
log group 4242
}
chain ih_filter {
meta l4proto tcp @ih,0,32 0x47455420 counter jump log_to_nflog
}
chain c_ingress {
type filter hook prerouting priority -150; policy accept;
jump ih_filter
}
chain c_egress {
type filter hook postrouting priority 150; policy accept;
jump ih_filter
}
}
在 Linux 上,日志设施可作为伪接口使用(libpcap和)tcpdump:
# tcpdump -D |grep nflog
12.nflog (Linux netfilter log (NFLOG) interface) [none]
选择的数据包nftables因此可以显示(重复使用相同的 NFLOG 组:4242):
tcpdump -n -s 0 -A -i nflog:4242
警告:与直接捕获相比,延迟更大。
注意:正确处理nftables具有扩展报头的情况实际上是用 UDP(而不是 TCP)和碎片数据包(由片段头) 使用socat
(在单独的网络命名空间中以防止nf_defrag_ipv6
启动),因为使用 UDP 比使用 TCP 更容易生成碎片数据包,并且我没有找到其他方法在某处使用扩展头。