【问题标题】:Do BPF filters work inside Docker containers?BPF 过滤器可以在 Docker 容器中工作吗?
【发布时间】:2020-08-28 15:37:05
【问题描述】:

我有一些 Python 代码可以打开一个套接字并使用 socket.setsockopt(..., SO_ATTACH_FILTER) 将 BPF 过滤器附加到它。不幸的是,生成过滤器的代码与其他东西有点纠结,但下面有一个可运行的大纲展示了我正在尝试做的事情。过滤器检查 EthernetType 字段是否为ETH_P_IP,IP 协议为IPPROTO_UDP,目标端口为 68 - 这应该将数据包限制为仅 DHCP 响应。

在主机上使用sudo python3 test.py 运行下面的脚本每次都会导致超时,除非我在超时发生之前手动执行 DHCP 租约更新(或者在网络上更新 DHCP 租约时非常偶尔)。但是,如果我在具有主机模式网络的 Docker 容器中做同样的事情,它几乎永远不会导致超时,总是会收到一个数据包,而且它几乎永远不会是 DHCP 响应。

docker 容器以 --privileged --net=host 并以 root 用户身份运行。

我必须做些什么才能让数据包过滤器在容器内工作吗?还是根本不可能?

import ctypes
import struct
import socket

ETH_P_ALL = 0x0003
SO_ATTACH_FILTER = 26

filters = bytes([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])

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

def main():
    sock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
    sock.bind(("eth0_bridge", ETH_P_ALL))
    sock.settimeout(2)
    sock.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, pf)
    try:
        data = sock.recv(1500)
    except:
        print('Timeout')
        exit(-1)
    print('No timeout')

main()

【问题讨论】:

  • filters 字节数组的第一行末尾缺少逗号。可以肯定的是,这只是一个复制粘贴错误吗?您的代码中有逗号吗?
  • tcpdump ip proto \\udp and dst port 68 在您的容器中工作吗?它应该使用相同的过滤器。
  • @Qeole - 是的,tcpdump 按预期工作。然而,事实证明这不是特定于 docker 容器,所以我要开始另一个问题。

标签: python linux docker bpf


【解决方案1】:

这里的问题是,BPF 过滤器仅在接收到数据时应用,而不是在进程从套接字缓冲区中取出数据时应用。因此,在绑定的套接字 (sock.bind(...)) 和正在应用的过滤器 (sock.setsockopt(...)) 之间有一个窗口,可以接收和缓冲与过滤器不匹配的数据包。随后对sock.recv(1500) 的调用将收到这样的数据包,即使它与过滤器不匹配。

避免这个问题的唯一方法似乎是使用这个序列:

  • 将套接字设置为非阻塞模式
  • 应用过滤器
  • 接收数据包,直到没有数据包可接收
  • 将套接字设置为阻塞模式
  • 设置套接字超时
  • 收到您感兴趣的数据包。

这会清除在绑定和正在应用的过滤器之间接收到的任何不匹配的数据包。当然,它也可能会丢失您感兴趣的数据包。这对我的场景来说不是问题,但可能是您的问题。

【讨论】:

    猜你喜欢
    • 2012-08-14
    • 2021-04-10
    • 2014-09-22
    • 1970-01-01
    • 2014-03-31
    • 2020-06-25
    • 2014-06-30
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多