gpt4 book ai didi

linux - BPF过滤器失败

转载 作者:行者123 更新时间:2023-12-03 09:51:23 25 4
gpt4 key购买 nike

谁能提出为什么这个(经典的)BPF 程序有时会让非 DHCP 响应数据包通过:

# Load the Ethertype field
BPF_LD | BPF_H | BPF_ABS 12
# And reject the packet if it's not 0x0800 (IPv4)
BPF_JMP | BPF_JEQ | BPF_K 0x0800 0 8

# Load the IP protocol field
BPF_LD | BPF_B | BPF_ABS 23
# And reject the packet if it's not 17 (UDP)
BPF_JMP | BPF_JEQ | BPF_K 17 0 6

# Check that the packet has not been fragmented
BPF_LD | BPF_H | BPF_ABS 20
BPF_JMP | BPF_JSET | BPF_K 0x1fff 4 0

# Load the IP header length field
BPF_LDX | BPF_B | BPF_MSH 14
# And load that offset + 16 to get the UDP destination port
BPF_LD | BPF_IND | BPF_H 16
# And reject the packet if the destination port is not 68
BPF_JMP | BPF_JEQ | BPF_K 68 0 1

# Accept the frame
BPF_RET | BPF_K 1500
# Reject the frame
BPF_RET | BPF_K 0

它不会让每一帧都通过,但在网络负载很重的情况下,它经常会失败。我正在用这个 Python 3 程序测试它:

import ctypes
import struct
import socket
ETH_P_ALL = 0x0003
SO_ATTACH_FILTER = 26
SO_ATTACH_BPF = 50
filters = [
0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x06, 0x11, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x45, 0x00, 0x04, 0x00, 0xff, 0x1f, 0x00, 0x00,
0xb1, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x15, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]

filters = bytes(filters)

b = ctypes.create_string_buffer(filters)
mem_addr_of_filters = ctypes.addressof(b)
pf = struct.pack("HL", 11, mem_addr_of_filters)
pf = bytes(pf)

def main():
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
sock.bind(("eth0", ETH_P_ALL))
sock.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, pf)
# sock.send(req)
sock.settimeout(1)
try:
data = sock.recv(1500)

if data[35] == 0x43:
return
print('Packet got through: 0x{:02x} 0x{:02x}, 0x{:02x}, 0x{:02x}'.format(data[12], data[13], data
except:
print('Timeout')
return
sock.close()

for ii in range(1000):
main()

如果我在将一个大的核心文件 SCP 发送到运行该脚本的主机时执行此操作,则在大多数(但不是所有)情况下它不会达到一秒超时。在较轻的负载下,故障要少得多——例如,在套接字接收时在 ssh 链接上闲逛;有时它可以顺利通过 1000 次迭代。

有问题的主机是 Linux 4.9.0。内核有 CONFIG_BPF=y

编辑

对于同一问题的更简单版本,为什么这个 BPF 程序完全允许任何数据包通过:

BPF_RET | BPF_K    0

编辑 2上面的测试是在 ARM64 机器上进行的。我在 amd64/Linux 5.9.0 上重新测试过。我仍然看到失败,虽然没有那么多。

最佳答案

我得到了 a response on LKML对此进行解释。

问题是过滤器是在帧到达界面时应用的,而不是在它通过 recv() 传递给用户空间时应用的。因此,在重负载下,帧在使用 socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) 创建套接字和使用 sock 应用过滤器之间到达.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, pf)。这些帧在队列中;应用过滤器后,后续到达的数据包将应用过滤器。

因此,一旦应用了过滤器,就必须先从套接字中“排出”任何排队的帧,然后才能依赖过滤器。

关于linux - BPF过滤器失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65568257/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com