【问题标题】:Why this program which use BPF and RAW SOCKET just hangs?为什么这个使用 BPF 和 RAW SOCKET 的程序会挂起?
【发布时间】:2019-07-16 08:52:24
【问题描述】:

目标:使用 BPF 编写一个简单的数据包过滤器。数据包过滤器应该允许您选择接口。

问题:如果我取消注释代码中倒数第三条指令(调用recvfrom,执行将挂起,我看不到任何输出(“缓冲区归零”,我应该可以在标准输出中看到)。

问题: 1) 我该如何解决? 2) 为什么程序在执行过程中挂起并且不显示第一个printf 输出? 3) 我如何从任何接口接收?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if.h>

#define DEFAULT_IF   "wlan0"

/* definisco programma bpf */
/* tcpdump -i lo icmp -dd */
struct sock_filter bpfcode[] = {
    { 0x28, 0, 0, 0x0000000c },   /* (000) ldh      [12] */
    { 0x15, 0, 3, 0x00000800 },   /* (001) jeq      #0x800   jt 2   jf 5 */
    { 0x30, 0, 0, 0x00000017 },   /* (002) ldb      [23] */
    { 0x15, 0, 1, 0x00000001 },   /* (003) jeq      #0x1     jt 4   jf 5 */
    { 0x6, 0, 0, 0x00040000 },    /* (004) ret      #262144 */
    { 0x6, 0, 0, 0x00000000 },    /* (005) ret      #0 */
};

int main(int argc, char *argv[])
{
    struct sock_fprog bpf = {
        sizeof(bpfcode) / sizeof(struct sock_filter),
        bpfcode
    };
    socklen_t saddr_len = sizeof(struct sockaddr_ll);
    struct sockaddr_ll addr;
    unsigned char *buffer;
    char ifname[IFNAMSIZ];
    int ret, sfd, rval;

    buffer = calloc(1, 65536);
    if (!buffer) {
        perror("calloc");
        return -1;
    }

    // prendi nome interfaccia
    if (argc > 1)
        strcpy(ifname, argv[1]);
    else
        strcpy(ifname, DEFAULT_IF);

    // creazione raw socket
    sfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (sfd < 0) {
        perror("socket");
        return -1;
    }

    // attacco filtro alla socket
    ret = setsockopt(sfd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
    if (ret < 0) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    // quando si usa packet socket bisogna settare sll_protocol e
    // sll_ifindex se si vuol fare il bind ad una specifica interfaccia
    memset(&addr, 0, sizeof(addr));
    addr.sll_family = AF_PACKET;
    addr.sll_protocol = htons(ETH_P_ALL);
    addr.sll_ifindex = if_nametoindex(ifname);
    printf("index %d", addr.sll_ifindex);

    //  viene assegnato un indirizzo al socket
    if (bind(sfd, (struct sockaddr *) &addr,
             sizeof(struct sockaddr_ll)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // ricevo traffico
    if (!buffer[0])
        printf("buffer zeroed");
    // rval = recvfrom(sfd, buffer, 65536, 0, (struct sockaddr *)&addr,
    //                 &saddr_len);
    if (buffer[0])
        printf("something was written in the buffer");

    return 0;
}

【问题讨论】:

    标签: sockets bpf


    【解决方案1】:
    1. 我该如何解决?

      您究竟想解决什么问题?见下文。

    2. 为什么程序在执行过程中挂起并且不显示第一个printf 输出?

      printf() 都可以工作,除非您没有在消息末尾打印任何换行符 ('\n'),因此系统不会将您的消息刷新到控制台。只需用换行符结束您的消息,您就会看到预期的消息。

      至于挂起,这仅仅是因为recvfrom() 一直等到数据包到达。好吧,在您的情况下,不仅仅是 any 数据包,因为您正在 ICMP 上进行过滤。从外部 ping 您的界面,程序应该会恢复。或者,为了调试你的 C 程序,只需在你的 BPF 程序中保留{ 0x6, 0, 0, 0x00040000 }(返回非零),任何接收到的数据包都应该这样做。

    3. 如何从任何接口接收?

      How to bind a socket to multiple interfaces

    【讨论】:

    • 谢谢我犯了一个愚蠢的错误。我忘记将“lo”作为参数传递,所以当我 ping 到 localhost 时什么都没有出现,因为 wlan0 被设置为默认接口。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-02
    • 2016-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多