C语言 BPF筛选对原始套接字不起作用

gv8xihay  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(76)

我想实现过滤tcp数据包中端口22上的所有数据我在代码中使用了tcpdump生成的bpf代码来应用它,但它不起作用
下面是我的测试代码

int main()
{
    /* From the example above: tcpdump -d host 127.0.0.1 and  port 22 -i lo */
    struct sock_filter code[] = {
        {0x28, 0, 0, 0x0000000c},
        {0x15, 0, 16, 0x00000800},
        {0x20, 0, 0, 0x0000001a},
        {0x15, 2, 0, 0x7f000001},
        {0x20, 0, 0, 0x0000001e},
        {0x15, 0, 12, 0x7f000001},
        {0x30, 0, 0, 0x00000017},
        {0x15, 2, 0, 0x00000084},
        {0x15, 1, 0, 0x00000006},
        {0x15, 0, 8, 0x00000011},
        {0x28, 0, 0, 0x00000014},
        {0x45, 6, 0, 0x00001fff},
        {0xb1, 0, 0, 0x0000000e},
        {0x48, 0, 0, 0x0000000e},
        {0x15, 2, 0, 0x00000016},
        {0x48, 0, 0, 0x00000010},
        {0x15, 0, 1, 0x00000016},
        {0x6, 0, 0, 0x00040000},
        {0x6, 0, 0, 0x00000000},
    };

    struct sock_fprog bpf = {
        .len    = ARRAY_SIZE(code),
        .filter = code,
    };

    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);

    int enable_hdrincl = 1;
    setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &enable_hdrincl, sizeof(enable_hdrincl));

    int ret = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));

    char buffer[4096];
    while (1) {
        memset(buffer, 0, sizeof(buffer));
        ssize_t recv_len = recv(sock, buffer, sizeof(buffer), 0);
        if (recv_len < 0) {
            perror("recv failed");
            close(sock);
            exit(-1);
        }
        printf("xxx\r\n");
    }
    return 0;
}

当我在命令行上使用tcpdump时,它会正常过滤

lcx@lcx-virtual-machine:/mnt/hgfs/share/tcp_demo/test$ sudo tcpdump  host 127.0.0.1 and  port 22 -i lo
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes
10:50:34.761640 IP lcx-virtual-machine.2222 > localhost.ssh: Flags [S], seq 1234, win 65535, length 0
pw9qyyiw

pw9qyyiw1#

示例sock_filter code中的分组偏移量指的是包括以太网报头的整个分组;由于测试程序中的套接字被设置为仅包括IP报头,所以偏移不匹配,并且过滤器失败。为了读取整个以太网数据包并能够使用给定的过滤器,可以使用

int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));

然后你可以把IP_HDRINCL也放进去。
如果出于某种奇怪的原因,您不想读取以太网报头,您必须调整BPF代码中的偏移量-括号中的数字以其人类可读的形式表示:

# tcpdump -d host 127.0.0.1 and  port 22 -i lo
(000) ldh      [12]
(001) jeq      #0x800           jt 2    jf 18
(002) ld       [26]
(003) jeq      #0x7f000001      jt 6    jf 4
(004) ld       [30]
(005) jeq      #0x7f000001      jt 6    jf 18
(006) ldb      [23]
(007) jeq      #0x84            jt 10   jf 8
(008) jeq      #0x6             jt 10   jf 9
(009) jeq      #0x11            jt 10   jf 18
(010) ldh      [20]
(011) jset     #0x1fff          jt 18   jf 12
(012) ldxb     4*([14]&0xf)
(013) ldh      [x + 14]
(014) jeq      #0x16            jt 17   jf 15
(015) ldh      [x + 16]
(016) jeq      #0x16            jt 17   jf 18
(017) ret      #262144
(018) ret      #0
  • 并禁用检查以太网协议ID的前两个指令;这导致代码:
{},
        {},
        {0x20, 0, 0, 0x0000000c},   // 26 -> 12
        {0x15, 2, 0, 0x7f000001},
        {0x20, 0, 0, 0x00000010},   // 30 -> 16
        {0x15, 0, 12, 0x7f000001},
        {0x30, 0, 0, 0x00000009},   // 23 -> 9
        {0x15, 2, 0, 0x00000084},
        {0x15, 1, 0, 0x00000006},
        {0x15, 0, 8, 0x00000011},
        {0x28, 0, 0, 0x00000006},   // 20 -> 6
        {0x45, 6, 0, 0x00001fff},
        {0xb1, 0, 0, 0x00000000},   // 14 -> 0
        {0x48, 0, 0, 0x00000000},   // 14 -> 0
        {0x15, 2, 0, 0x00000016},
        {0x48, 0, 0, 0x00000002},   // 16 -> 2
        {0x15, 0, 1, 0x00000016},
        {0x6, 0, 0, 0x00040000},
        {0x6, 0, 0, 0x00000000},

相关问题